Testing and Validation

Terratest Setup

// test/main_test.go
package test
 
import (
    "testing"
    "github.com/gruntwork-io/terratest/modules/gcp"
    "github.com/gruntwork-io/terratest/modules/terraform"
    "github.com/stretchr/testify/assert"
)
 
func TestTerraformBasicExample(t *testing.T) {
    t.Parallel()
 
    terraformOptions := &terraform.Options{
        TerraformDir: "../examples/basic",
        Vars: map[string]interface{}{
            "project_id": "test-project",
            "region":     "us-central1",
        },
        EnvVars: map[string]string{
            "GOOGLE_CREDENTIALS": "${file("credentials.json")}",
        },
    }
 
    defer terraform.Destroy(t, terraformOptions)
    terraform.InitAndApply(t, terraformOptions)
 
    instanceID := terraform.Output(t, terraformOptions, "instance_id")
    assert.NotEmpty(t, instanceID)
}

Unit Testing with tftest

# test/variables_test.go
func TestVariableValidation(t *testing.T) {
    cases := []struct {
        name          string
        variableValue interface{}
        expectError   bool
    }{
        {
            name:          "valid_instance_type",
            variableValue: "n1-standard-1",
            expectError:   false,
        },
        {
            name:          "invalid_instance_type",
            variableValue: "invalid-type",
            expectError:   true,
        },
    }
 
    for _, tc := range cases {
        t.Run(tc.name, func(t *testing.T) {
            _, errs := terraform.ValidateVarValue(
                "instance_type",
                tc.variableValue,
            )
            hasError := len(errs) > 0
            assert.Equal(t, tc.expectError, hasError)
        })
    }
}

CI/CD Integration

GitHub Actions Workflow

# .github/workflows/terraform.yml
name: 'Terraform CI/CD'
 
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
 
jobs:
  terraform:
    name: 'Terraform'
    runs-on: ubuntu-latest
 
    steps:
    - name: Checkout
      uses: actions/checkout@v2
 
    - name: Setup Terraform
      uses: hashicorp/setup-terraform@v1
      with:
        terraform_version: 1.0.0
 
    - name: Terraform Format
      run: terraform fmt -check -recursive
 
    - name: Terraform Init
      run: |
        terraform init \
          -backend-config="bucket=${{ secrets.TF_STATE_BUCKET }}" \
          -backend-config="prefix=terraform/state"
 
    - name: Terraform Validate
      run: terraform validate
 
    - name: Terraform Plan
      if: github.event_name == 'pull_request'
      run: |
        terraform plan \
          -var="project_id=${{ secrets.GCP_PROJECT_ID }}" \
          -var="credentials=${{ secrets.GCP_CREDENTIALS }}" \
          -out=tfplan
      
    - name: Terraform Apply
      if: github.ref == 'refs/heads/main' && github.event_name == 'push'
      run: terraform apply -auto-approve tfplan

Advanced Module Patterns

Composite Module Pattern

# modules/application/main.tf
module "networking" {
  source = "../networking"
 
  project_id = var.project_id
  region     = var.region
  vpc_config = var.vpc_config
}
 
module "compute" {
  source = "../compute"
 
  depends_on = [module.networking]
 
  project_id = var.project_id
  network_id = module.networking.vpc_id
  subnet_id  = module.networking.subnet_ids["main"]
}
 
module "load_balancer" {
  source = "../load_balancer"
 
  depends_on = [module.compute]
 
  project_id     = var.project_id
  instance_group = module.compute.instance_group
  ssl_cert       = var.ssl_certificate
}

Factory Pattern Module

# modules/instance_factory/main.tf
locals {
  instance_configs = {
    for instance in var.instances : instance.name => merge(
      var.default_config,
      instance
    )
  }
}
 
resource "google_compute_instance" "instances" {
  for_each = local.instance_configs
 
  name         = each.key
  machine_type = each.value.machine_type
  zone         = each.value.zone
 
  boot_disk {
    initialize_params {
      image = each.value.image
      size  = each.value.disk_size
    }
  }
 
  network_interface {
    network    = each.value.network
    subnetwork = each.value.subnetwork
  }
 
  tags = concat(
    var.common_tags,
    each.value.additional_tags
  )
 
  metadata = merge(
    var.common_metadata,
    each.value.additional_metadata
  )
}

Resource Wrapper Pattern

# modules/gke_cluster_wrapper/main.tf
resource "google_container_cluster" "cluster" {
  name     = var.cluster_name
  location = var.region
 
  # Remove default node pool
  remove_default_node_pool = true
  initial_node_count       = 1
 
  dynamic "private_cluster_config" {
    for_each = var.private_cluster ? [1] : []
    content {
      enable_private_nodes    = true
      enable_private_endpoint = var.private_endpoint
      master_ipv4_cidr_block = var.master_ipv4_cidr
    }
  }
 
  dynamic "master_authorized_networks_config" {
    for_each = length(var.authorized_networks) > 0 ? [1] : []
    content {
      dynamic "cidr_blocks" {
        for_each = var.authorized_networks
        content {
          cidr_block   = cidr_blocks.value.cidr
          display_name = cidr_blocks.value.name
        }
      }
    }
  }
 
  addons_config {
    http_load_balancing {
      disabled = !var.enable_http_load_balancing
    }
    horizontal_pod_autoscaling {
      disabled = !var.enable_horizontal_pod_autoscaling
    }
  }
}

Monitoring and Alerting

# monitoring.tf
resource "google_monitoring_dashboard" "dashboard" {
  dashboard_json = jsonencode({
    displayName = "Infrastructure Dashboard"
    gridLayout = {
      widgets = [
        {
          title = "CPU Usage"
          xyChart = {
            dataSets = [{
              timeSeriesQuery = {
                timeSeriesFilter = {
                  filter = "metric.type=\"compute.googleapis.com/instance/cpu/utilization\""
                }
              }
            }]
          }
        },
        {
          title = "Memory Usage"
          xyChart = {
            dataSets = [{
              timeSeriesQuery = {
                timeSeriesFilter = {
                  filter = "metric.type=\"compute.googleapis.com/instance/memory/utilization\""
                }
              }
            }]
          }
        }
      ]
    }
  })
}
 
resource "google_monitoring_alert_policy" "alert_policy" {
  display_name = "High CPU Usage Alert"
  combiner     = "OR"
  conditions {
    display_name = "CPU Usage > 80%"
    condition_threshold {
      filter     = "metric.type=\"compute.googleapis.com/instance/cpu/utilization\""
      duration   = "300s"
      comparison = "COMPARISON_GT"
      threshold_value = 0.8
    }
  }
 
  notification_channels = [
    google_monitoring_notification_channel.email.name
  ]
}

Would you like me to continue with more advanced topics such as custom providers, workspaces management, or specific GCP service configurations?