How to use Terraform to create a vpc network and a Compute instance in GCP

In this guide, we will build a simple compute instance(virtual machine) in Google cloud platform using terraform. Terraform allows you to develop cloud infrastructure by automating repetitive tasks.

If you have a use case where you need to create multiple instances, all of which have different memories, disk sizes, and operating systems, the process of going to the GCP Console UI and manually clicking buttons to set up each server can be tiring and time consuming. Terraform was created to solve that problem. It allows you to have the instructions as code that can be used to plan, deploy, modify, and destroy all of our systems.

Checkout these:

Requirements

You need the following to proceed

  • A Google Project – GCP organizes resources into projects. Create one now in the GCP console and make note of the project ID. 

  • Enable Google Compute Engine for your project in the GCP console. Make sure to select the project you are using to follow this tutorial and click the “Enable” button.

  • A GCP service account key: Create a service account key to enable Terraform to access your GCP account. When creating the key, use the following settings:

  • Select the project you created in the previous step.

  • Click “Create Service Account”.

  • Give it any name you like and click “Create”.

  • For the Role, choose “Project -> Editor”, then click “Continue”.

  • Skip granting additional users access, and click “Done”.

After you create your service account, download your service account key.

  • Select your service account from the list.
  • Select the “Keys” tab.
  • In the drop down menu, select “Create new key”.
  • Leave the “Key Type” as JSON.
  • Click “Create” to create the key and save the key file to your system.

Step 1 – Downloading and installing terraform

Terraform is available as a binary for most distributions. Get the latest binary and download instructions from terraform downloads page here.

Step 2 – Adding the Project code

In this section we will create the files that will contain the code for our resources. First you need to create a directory and switch to it. In your terminal use these commands:

mkdir gcp-compute
cd gcp-compute

First we will have to specify the providers. Terraform relies on plugins called “providers” to interact with cloud providers, SaaS providers, and other APIs.

Terraform configurations must declare which providers they require so that Terraform can install and use them. Additionally, some providers require configuration (like endpoint URLs or cloud regions) before they can be used.

This is the main.tf where we define the google provider that we will use and we are also specifying the specific versions. We are also defining some locals that we can reuse.

A local value assigns a name to an expression, so you can use the name multiple times within a module instead of repeating the expression. Local values are like a function’s temporary local variables.

locals {
  env              = "dev"
  project          = "citizix"
  credentials_path = "./gcp-credentials.json"
  region           = "europe-west1"
}

terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = ">=4.20.0, < 5.0.0"
    }
  }
}

provider "google" {
  credentials = file(local.credentials_path)

  project = "citizix-prj"
  region  = local.region
}

Create a vpc

Next we will need to create a vpc because all the other resources depends on it. The following code specifies a google compute network and two sub networks – one private and one public. Save it as vpc.tf.

locals {
  vpc_name = "${local.env}-${local.project}-vpc"
}

resource "google_compute_network" "vpc" {
  name                    = local.vpc_name
  auto_create_subnetworks = "false"
}

resource "google_compute_subnetwork" "public" {
  name          = "${local.vpc_name}-public-0"
  region        = local.region
  network       = google_compute_network.vpc.name
  ip_cidr_range = "10.1.0.0/24"
}

resource "google_compute_subnetwork" "private" {
  name                     = "${local.vpc_name}-private-0"
  region                   = local.region
  private_ip_google_access = true
  network                  = google_compute_network.vpc.name
  ip_cidr_range            = "10.1.1.0/24"
}

Create the compute instance

Now we can add the compute instance. The below code defines ssh keys that we want to use then a compute instance that we name rocky in the region specified. We are using n1-standard-1 machine type. You can list machine types using this command

gcloud compute machine-types list

We are also specifying a zone from the region we defined earlier. Use this gcloud command to query a zone in your region:

gcloud compute zones list

For boot disk, we are using a rocky linux image. You can get a list of images using this gcloud command:

gcloud compute images list

The meta startup script specifies some script to execute on initialization. Finally, we add some tags that we can use to target when creating firewall rules.

Save this in a file called instance.tf.

locals {
  ssh_keys = [
    {
      username   = "eutychus"
      public_key = "~/.ssh/id_rsa.pub"
    },
    {
      username   = "citizix"
      public_key = "~/.ssh/id_citizix.pub"
    }
  ]
}

resource "google_compute_instance" "rocky" {
  name                      = "${local.env}-${local.project}-rockysrv"
  machine_type              = "n1-standard-1"
  zone                      = "${local.region}-a"
  allow_stopping_for_update = true

  boot_disk {
    initialize_params {
      image = "rocky-linux-cloud/rocky-linux-8"
    }
  }

  metadata_startup_script = <<EOF
#!/bin/bash -e
sudo dnf -y update
sudo hostnamectl set-hostname rockysrv.citizix.com
sudo dnf install -y vim wget curl
EOF

  network_interface {
    network    = google_compute_network.vpc.name
    subnetwork = google_compute_subnetwork.public.name

    access_config {
    }
  }

  metadata = {
    ssh-keys = join("\n", [for key in local.ssh_keys : "${key.username}:${file(key.public_key)}"])
  }

  tags = ["allow-web", "allow-ssh"]
}

output "rocky-nat-ip" {
  value = google_compute_instance.rocky.network_interface.0.access_config.0.nat_ip
}

output "rocky-ip" {
  value = google_compute_instance.rocky.network_interface.0.network_ip
}

Creating a firewall

Add firewall rules to allow ssh and http. Save this in firewall.tf

resource "google_compute_firewall" "ssh" {
  name = "allow-ssh"
  allow {
    ports    = ["22"]
    protocol = "tcp"
  }
  direction     = "INGRESS"
  network       = google_compute_network.vpc.name
  priority      = 1000
  source_ranges = ["0.0.0.0/0"]
  target_tags   = ["allow-ssh"]
}

resource "google_compute_firewall" "http-server" {
  name    = "allow-web"
  network = google_compute_network.vpc.name

  allow {
    protocol = "tcp"
    ports    = ["80"]
  }

  allow {
    protocol = "tcp"
    ports    = ["443"]
  }

  // Allow traffic from everywhere to instances with an http-server tag
  source_ranges = ["0.0.0.0/0"]
  target_tags   = ["allow-web"]
}

Step 4 – Planning and applying changes

To apply the changes, do the following

First initialize terraform to download required dependencies and plugins.

terraform init

Then validate to ensure that you have valid code without errors.

terraform validate

Then plan to confirm that the changes being introduced are what is expected.

terraform plan -out tf.plan

Finally apply to create resources in gcp.

terraform apply - tf.plan

To apply with no prompt

terraform apply -auto-approve tf.plan

If you no longer need the changes you can destroy with this. You can add -auto-approve if you do not want to be prompted.

terraform destroy

Conclusion

We were able to use terraform to create a vpc, instance and firewall rules to create resources in gcp. This allows us to create and destroy resources easily at the same time bringing in benefits of having infrastructure as code.

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy