I’ll break this into multiple parts due to the extensive nature of Terraform configuration and best practices.
Part 1: Basic Structure and Provider Configuration
Directory Structure
project/
├── main.tf # Main configuration file
├── variables.tf # Variable declarations
├── outputs.tf # Output declarations
├── terraform.tfvars # Variable values
├── provider.tf # Provider configuration
├── backend.tf # Backend configuration
└── modules/
├── networking/
├── compute/
└── storage/
Provider Configuration
# provider.tf
terraform {
required_version = ">= 1.0.0"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 4.0"
}
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
# GCP Provider
provider "google" {
project = var.project_id
region = var.region
zone = var.zone
credentials = file(var.credentials_file)
}
# AWS Provider
provider "aws" {
region = var.aws_region
access_key = var.aws_access_key
secret_key = var.aws_secret_key
default_tags {
tags = {
Environment = var.environment
Project = var.project_name
ManagedBy = "Terraform"
}
}
}
Backend Configuration
# backend.tf
terraform {
backend "gcs" {
bucket = "tf-state-prod"
prefix = "terraform/state"
credentials = "path/to/credentials.json"
}
}
# Alternative S3 backend
terraform {
backend "s3" {
bucket = "tf-state-prod"
key = "terraform/state/prod.tfstate"
region = "us-west-1"
encrypt = true
dynamodb_table = "terraform-lock"
}
}
Variables and Outputs
# variables.tf
variable "project_id" {
description = "GCP Project ID"
type = string
}
variable "environment" {
description = "Environment name"
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be dev, staging, or prod."
}
}
variable "machine_types" {
description = "Machine types for different environments"
type = map(string)
default = {
dev = "n1-standard-1"
staging = "n1-standard-2"
prod = "n1-standard-4"
}
}
# outputs.tf
output "vpc_id" {
description = "ID of the VPC"
value = module.networking.vpc_id
}
output "instance_ips" {
description = "Public IPs of instances"
value = module.compute.instance_ips
sensitive = true
}
Main Configuration
# main.tf
locals {
common_labels = {
environment = var.environment
project = var.project_id
managed_by = "terraform"
}
network_name = "vpc-${var.environment}"
}
module "networking" {
source = "./modules/networking"
project_id = var.project_id
region = var.region
vpc_name = local.network_name
subnets = var.subnet_configs
labels = local.common_labels
}
module "compute" {
source = "./modules/compute"
depends_on = [module.networking]
project_id = var.project_id
network_id = module.networking.vpc_id
machine_type = var.machine_types[var.environment]
instance_name = "app-${var.environment}"
labels = local.common_labels
}
Module Structure
Networking Module
# modules/networking/main.tf
resource "google_compute_network" "vpc" {
name = var.vpc_name
auto_create_subnetworks = false
project = var.project_id
}
resource "google_compute_subnetwork" "subnets" {
for_each = var.subnets
name = each.key
ip_cidr_range = each.value.cidr
region = var.region
network = google_compute_network.vpc.id
project = var.project_id
secondary_ip_range = each.value.secondary_ranges
dynamic "log_config" {
for_each = var.enable_flow_logs ? [1] : []
content {
aggregation_interval = "INTERVAL_5_SEC"
flow_sampling = 0.5
metadata = "INCLUDE_ALL_METADATA"
}
}
}
# modules/networking/variables.tf
variable "vpc_name" {
description = "Name of the VPC"
type = string
}
variable "subnets" {
description = "Subnet configurations"
type = map(object({
cidr = string
secondary_ranges = list(object({
range_name = string
ip_cidr_range = string
}))
}))
}
# modules/networking/outputs.tf
output "vpc_id" {
description = "ID of the created VPC"
value = google_compute_network.vpc.id
}
output "subnet_ids" {
description = "Map of subnet names to IDs"
value = {
for name, subnet in google_compute_subnetwork.subnets :
name => subnet.id
}
}
Compute Module
# modules/compute/main.tf
resource "google_compute_instance" "vm" {
name = var.instance_name
machine_type = var.machine_type
zone = var.zone
project = var.project_id
boot_disk {
initialize_params {
image = var.boot_image
size = var.boot_disk_size
type = var.boot_disk_type
}
}
network_interface {
network = var.network_id
subnetwork = var.subnet_id
dynamic "access_config" {
for_each = var.assign_public_ip ? [1] : []
content {
// Ephemeral public IP
}
}
}
metadata_startup_script = file(var.startup_script)
service_account {
email = var.service_account_email
scopes = var.service_account_scopes
}
labels = var.labels
lifecycle {
prevent_destroy = var.environment == "prod"
}
}
# Instance group
resource "google_compute_instance_group" "group" {
count = var.create_instance_group ? 1 : 0
name = "${var.instance_name}-group"
description = "Instance group for ${var.instance_name}"
zone = var.zone
network = var.network_id
instances = [google_compute_instance.vm.self_link]
named_port {
name = "http"
port = 80
}
}
[Continue to Part 2 with more advanced configurations, state management, and best practices?]
Would you like me to continue with the next part covering more advanced Terraform configurations, state management, and best practices?