Terraform on Google Cloud V1.1 — Deploying VM with Github actions

Creating GCP compute engine VM in terraform with Github Actions

Akhilesh Mishra
Google Cloud - Community

--

This blog is continuation to my last blog. In this i will deploy the VM in GCP with automated CI pipeline using Github Actions. I recommend you to go through my previous blog before this.

Photo by Sigmund on Unsplash

In the last blog in this series, i used Terraform to deploy network, subnet, storage bucket in GCP by manually running Terraform commands.

Prerequisites:

  • You should have a Github account, have installed git and created access token. You should also have basic knowledge of git.
  • You should have setup Google Cloud account and followed my last blog for basic Terraform config.

Generate service account key and create Github secret

I will use the same service account for this blog.

PROJECT_ID = "my-project-id"
# set the project in cloud shell.
gcloud config set project $PROJECT_ID

# Creating service account
gcloud iam service-accounts create somesvc \
--description="DESCRIPTION" \
--display-name="resource-manager-list"

# Assigning project level owner role to service account
gcloud projects add-iam-policy-binding \
someproject --member="serviceAccount:somesvc@${PROJECT_ID}.iam.gserviceaccount.com" \
--role="roles/owner"

# Creating key
gcloud iam service-accounts keys create somesvc.json --iam-account \
somesvc@${PROJECT_ID}.iam.gserviceaccount.com

This will generate a key somesvc.json, copy the content of the file.

Go to Github repo > settings > secrets and variable > drop down >actions> new repository secrets
name -> GCLOUD_SERVICE_ACCOUNT_KEY
key -> <Paste the key file content>

Note: This practice of storing service account key in secrets to run CI pipeline is not recommended. It can be done in secure way with keyless authentication with github using GCP Workload identity federation and ‘google-github-actions/auth@v0’ Github action. I will use this method in my next blog

Lets go to the directory where you created basic terraform config and intialize the git with git init.

Add below code to variables.tf

variable "zone" {
type = string
description = "GCP zone for vm"
}

variable "vm_machine_type" {
type = string
description = "machine type for vm"
}
variable "prefix" {
type = string
default = "main"
}

Below lines to terraform.tfvars

zone ="europe-west2-b"
vm_machine_type="e2-standard-4"

Create a new file in root directory — vm.tf


# Create service account for vm
resource "google_service_account" "default" {
project = var.project_id
account_id = "${var.prefix}-medium-vm-sa"
display_name = "VM SA"
}

# VM config
resource "google_compute_instance" "default" {
name = "medium-vm"
project = var.project_id
machine_type = var.vm_machine_type
zone = var.zone
tags = ["ssh"]
allow_stopping_for_update = true

boot_disk {
initialize_params {
image = "debian-cloud/debian-11"
type = "pd-ssd"
size = 100
}
}

# network config
network_interface {
network = var.subnetwork_name
subnetwork = var.vpc_name
subnetwork_project = var.project_id
}

service_account {
email = google_service_account.default.email
scopes = ["cloud-platform"]
}
}

Now we will add Ingress firewall rules to enable ssh into vm and Egress for connecting to internet for patchs updates.

What are the firewall rules in GCP?

Firewall rules in Google Cloud Platform (GCP) are a set of policies that control inbound and outbound traffic to and from your virtual machine instances (VMs) and other resources within a Virtual Private Cloud (VPC) network.

Create firewall.tf and paste below lines of code into it.

#Ingress firewall rules
resource "google_compute_firewall" "ssh" {
name = "vm-ssh"
network = var.vpc_name
project = var.project_id

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

target_tags = ["ssh"]
source_ranges = ["0.0.0.0/0"]
direction = "INGRESS"
priority = "20"
}

# Egress rules
resource "google_compute_firewall" "egress" {
name = "egress-firewall"
network = var.vpc_name
project = var.project_id
priority = 1000

direction = "EGRESS"

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

source_ranges = ["0.0.0.0/0"]
}

Since we have not created any external IP for our vm, it cannot connect connect to internet to apply patches. To enable the internet connectivity with Internal IP, we will create NAT gateway. Using NAT, vm can connect to internet without any external IP.

What is Cloud NAT?

Cloud NAT (Network Address Translation) is a Google Cloud Platform (GCP) service that provides outbound network connectivity for virtual machine (VM) instances in private networks. It allows VM instances with private IP addresses to access the internet and other Google Cloud services.

To create cloud NAT, we also need Cloud router

What is Cloud Router?

Cloud Router is a networking service offered by Google Cloud Platform (GCP) that provides dynamic routing and connectivity between virtual private clouds (VPCs) within the Google Cloud environment, as well as between on-premises networks and Google Cloud.

Add below lines of code into firewall.tf to deploy Cloud Router and Cloud NAT.

resource "google_compute_router" "nat_router" {
name = "my-nat-router"
network = google_compute_network.default.self_link

nat {
source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES"
nat_ip_allocate_option = "AUTO_ONLY"
}
}


resource "google_compute_router_nat" "nat_gateway" {
name = "my-nat-gateway"
router = google_compute_router.nat_router.name
nat_ip_allocate_option = "AUTO_ONLY"

source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS"

subnet {
name = google_compute_subnetwork.default.name
source_ip_ranges_to_nat = ["10.0.0.0/24"]
}
}

Now our terraform configuration is ready to deploy.

We need to write a Github workflow to deploy the Terraform config to GCP

Create a .github directory in root directory and create workflow directory inside it. Inside workflow directory, we will create deploy.yml file that will have the CI workflow code.

mkdir -p .github/workflow
touch github/workflow/deploy.yml

Add the below code to deploy.yml file

name: Terraform Deployment
on:
push:
branches:
- main
env:
PROJECT_ID: your_project_id

jobs:
terraform:
runs-on: ubuntu-latest
permissions:
contents: 'read'
id-token: 'write'

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Set up Google Cloud SDK
uses: 'google-github-actions/setup-gcloud@v1'
with:
version: '>= 363.0.0'
service_account_key: ${{ secrets.GCLOUD_SERVICE_ACCOUNT_KEY }}
project_id: ${{env.PROJECT_ID}}

- name: Configure Terraform
run: |
echo ${{ secrets.GCLOUD_SERVICE_ACCOUNT_KEY }} > key.json
gcloud auth activate-service-account --key-file=key.json
gcloud config set project YOUR_PROJECT_ID
gcloud config set compute/zone YOUR_COMPUTE_ZONE
rm -f key.json

- name: Initialize Terraform
run: terraform init

- name: Validate Terraform configuration
run: terraform validate

- name: Plan Terraform deployment
run: terraform plan -out=tfplan

- name: Apply Terraform changes
run: terraform apply tfplan -auto-approve

- name: Clean up Terraform artifacts
run: rm -f tfplan

We are good now. let’s push this code to Github repo to main branch, and it will trigger the CI pipeline that will deploy the terraform resources to GCP project.

You can see the logs

git add .
git commit -m "vm on gcp"
git push origin main

In most cases, it is not recommended to directly push to main branch. You should create a dev branch, push to that branch and create PR to merge to main.

If you found my content helpful, buy me a coffee to show support.

Connect with me in Linkedin — @akhilesh-mishra-0ab886124
Follow me for more content on google cloud, terraform, Python and other Devops tools.

--

--

Akhilesh Mishra
Google Cloud - Community

Self taught DevOps engineer with expertise in multi-cloud, and various DevOps tools. Open for mentorship - https://topmate.io/akhilesh_mishra