Technical Theory

Creating a Debian VM with Startup Script and Metadata in GCP using Terraform

Introduction

This tutorial guides you through creating a Debian Linux virtual machine in Google Cloud Platform (GCP) using Terraform. You’ll learn how to:

  • Define infrastructure as code with Terraform.
  • Create a Debian VM instance.
  • Add a startup script to configure the VM on boot.
  • Set custom metadata for the instance.
  • Use a dedicated service account for the VM.
  • Utilize Free Tier resources where applicable.

Prerequisites:

  • A Google Cloud Platform (GCP) account with billing enabled.
  • The Google Cloud SDK (gcloud) installed and configured.
  • Terraform installed.
  • Basic understanding of Terraform syntax and concepts.

Task 1: Project Setup and Authentication

First, set up your GCP project and authenticate Terraform.

  1. Set the Project ID: Replace your-project-id with your actual GCP project ID.

    NODE_TYPE // bash
    gcloud config set project your-project-id
  2. Authenticate with GCP: This command will open a browser window to authenticate.

    NODE_TYPE // bash
    gcloud auth application-default login
  3. Create a Terraform Project Directory: Create a directory for your Terraform configuration files.

    NODE_TYPE // bash
    mkdir gcp-debian-vm && cd gcp-debian-vm

Task 2: Terraform Configuration Files

Create the following Terraform configuration files.

  1. main.tf: This file defines the core resources: the Google Compute Engine instance and associated configurations.

    NODE_TYPE // terraform
    terraform {
      required_providers {
        google = {
          source  = "hashicorp/google"
          version = "~> 5.0"
        }
      }
    }
    
    provider "google" {
      project = "your-project-id"  # Replace with your Project ID
      region  = "us-central1"
    }
    
    resource "google_compute_network" "vpc_network" {
      name                    = "terraform-network"
      auto_create_subnetworks = false
    }
    
    resource "google_compute_subnetwork" "default" {
      name          = "terraform-subnet"
      ip_cidr_range = "10.10.10.0/24"
      network       = google_compute_network.vpc_network.id
      region        = "us-central1"
    }
    
    resource "google_compute_address" "static" {
        name          = "static-ip"
        address_type  = "EXTERNAL"
        prefix_length = 20
        region        = "us-central1"
    }
    resource "google_compute_firewall" "firewall" {
      name    = "terraform-firewall"
      network = google_compute_network.vpc_network.name
    
      allow {
        protocol = "tcp"
        ports    = ["22", "80", "443"]
      }
    
      source_ranges = ["0.0.0.0/0"]
    }
    
    resource "google_service_account" "default" {
      account_id   = "debian-vm-sa"
      display_name = "Debian VM Service Account"
    }
    
    resource "google_project_iam_member" "default" {
      project = "your-project-id" # Replace with your Project ID
      role    = "roles/logging.logWriter"
      member  = "serviceAccount:${google_service_account.default.email}"
    }
    resource "google_project_iam_member" "default_storage" {
      project = "your-project-id" # Replace with your Project ID
      role    = "roles/storage.objectViewer"
      member  = "serviceAccount:${google_service_account.default.email}"
    }
    
    resource "google_compute_instance" "default" {
      name         = "debian-vm"
      machine_type = "e2-micro"  # Use a Free Tier machine type
      zone         = "us-central1-a"
    
      boot_disk {
        initialize_params {
          image = "debian-cloud/debian-11"  # Debian 11
        }
      }
    
      network_interface {
        subnetwork = google_compute_subnetwork.default.id
        access_config {
          network_tier = "PREMIUM"
          nat_ip = google_compute_address.static.address
        }
      }
    
      metadata = {
        "startup-script" = file("startup.sh")
      }
    
      service_account {
        email  = google_service_account.default.email
        scopes = ["cloud-platform"]
      }
       depends_on = [
        google_project_iam_member.default,
        google_project_iam_member.default_storage
      ]
    }
  2. startup.sh: This script will execute when the VM instance starts. For this example, it will update the package list, install nginx, and start the service.

    NODE_TYPE // bash
    #!/bin/bash
    apt-get update
    apt-get install -y nginx
    systemctl start nginx
  3. variables.tf (Optional): This file can hold variable definitions to make your configuration more reusable.

    NODE_TYPE // terraform
    variable "project_id" {
      type = string
      description = "The GCP project ID"
    }
    
    variable "region" {
      type = string
      default = "us-central1"
      description = "The GCP region to deploy resources in"
    }
    
    variable "zone" {
      type = string
      default = "us-central1-a"
      description = "The GCP zone to deploy the VM in"
    }

    If you create this file, update main.tf to use the variables:

    NODE_TYPE // terraform
    provider "google" {
      project = var.project_id
      region  = var.region
    }
    
    resource "google_compute_instance" "default" {
      name         = "debian-vm"
      machine_type = "e2-micro"
      zone         = var.zone
      # ...
    }
  4. terraform.tfvars (Optional): If you created variables.tf, you can define the values of your variables here. Important: Do not commit this file to version control if it contains sensitive information!

    NODE_TYPE // terraform
    project_id = "your-project-id" # Replace with your Project ID

Task 3: Initialize, Plan, and Apply

Now, use Terraform to create the infrastructure.

  1. Initialize Terraform:

    NODE_TYPE // bash
    terraform init

    This command downloads the necessary provider plugins (in this case, the Google Cloud provider).

    NODE_TYPE // output
    Initializing the backend...
    
    Initializing provider plugins...
    - Finding hashicorp/google versions matching "~> 5.0"...
    - Installing hashicorp/google v5.X.Y...
    - Installed hashicorp/google v5.X.Y (signed by HashiCorp)
    
    Terraform has been successfully initialized!
    
    You may now begin working with Terraform. Try running "terraform plan" to see
    any changes that are required for your infrastructure. All Terraform commands
    should now work.
    
    If you ever set or change modules or backend configuration for Terraform,
    rerun this command after saving the changes.
  2. Create an Empty startup.sh File

    NODE_TYPE // bash
    touch startup.sh

    This is necessary because Terraform will read and inject the contents of this file into the metadata of the compute resource.

  3. Plan:

    NODE_TYPE // bash
    terraform plan

    This command shows you the changes Terraform will make to your infrastructure. Review the plan carefully. If you are not using variables, you’ll need to replace your-project-id directly in main.tf before running terraform plan.

    NODE_TYPE // output
    Terraform will perform the following actions:
    
      # google_compute_instance.default will be created
      + resource "google_compute_instance" "default" {
          # ... (Output showing planned changes)
        }
    
    Plan: 1 to add, 0 to change, 0 to destroy.
  4. Apply:

    NODE_TYPE // bash
    terraform apply

    This command applies the changes defined in your Terraform configuration. Type yes when prompted to confirm.

    NODE_TYPE // output
    Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
    
    Outputs:
    
    instance_ip = "34.X.X.X"

Task 4: Verify the Deployment

  1. Get the External IP: After the terraform apply command completes, it will output the external IP address of the VM. You can also find it in the Google Cloud Console.

  2. Access the VM via SSH:

    NODE_TYPE // bash
    gcloud compute ssh debian-vm --zone us-central1-a
  3. Check Nginx: Once connected via SSH, verify that Nginx is running:

    NODE_TYPE // bash
    sudo systemctl status nginx

    You should see output indicating that the Nginx service is active.

  4. Test the VM in a Browser: Open a web browser and navigate to the external IP address of your VM. You should see the default Nginx welcome page.

Task 5: Cleanup

When you’re finished, destroy the resources to avoid incurring unnecessary charges.

NODE_TYPE // bash
terraform destroy

Type yes when prompted to confirm.

Conclusion

In this tutorial, you learned how to create a Debian Linux virtual machine in Google Cloud Platform (GCP) using Terraform. You configured a startup script to automate initial setup, customized instance metadata, and assigned a service account for secure access to other GCP services. This example demonstrates a foundational infrastructure setup that can be extended for more complex applications and deployments.

Next Topic