Skip to main content

Command Palette

Search for a command to run...

Deploying Multi-Cloud Infrastructure with Terraform Modules

Updated
5 min readView as Markdown

Modern infrastructure is no longer confined to a single region, account, or even cloud provider. As systems scale, the ability to orchestrate resources across multiple environments becomes essential. Terraform’s provider system enables exactly this—but only if you understand how to structure modules correctly.

In this post, I’ll walk through:

  • The provider alias pattern for reusable modules

  • How configuration_aliases works

  • Wiring providers into modules using the providers map

  • A Docker-based quick start

  • And finally, deploying a production-ready Kubernetes workload on Amazon EKS using Terraform


Why Provider Design Matters in Terraform

Terraform separates infrastructure logic (modules) from environment configuration (providers).

This separation becomes critical when:

  • Deploying across multiple AWS regions

  • Working with multiple AWS accounts

  • Combining different providers (AWS, Kubernetes, Docker)

A common mistake is trying to define providers inside modules. This breaks reusability and makes modules rigid.

The correct pattern: 👉 Providers are defined in the root module and passed into child modules


The Provider Alias Pattern for Modules

When working with multiple regions or accounts, you define multiple provider instances using aliases.

Root Module

provider "aws" {
  alias  = "primary"
  region = "us-east-1"
}

provider "aws" {
  alias  = "replica"
  region = "us-west-2"
}

These represent two distinct AWS environments.


Module Definition

terraform {
  required_providers {
    aws = {
      source                = "hashicorp/aws"
      version               = "~> 5.0"
      configuration_aliases = [aws.primary, aws.replica]
    }
  }
}

Key Insight: configuration_aliases

This tells Terraform:

  • “This module expects multiple AWS provider instances”

  • “Specifically, aliases named primary and replica

Without this, Terraform cannot safely map providers into the module.


Wiring Providers into the Module

module "multi_region_app" {
  source   = "../../modules/multi-region-app"
  app_name = "my-app"

  providers = {
    aws.primary = aws.primary
    aws.replica = aws.replica
  }
}

This providers map explicitly connects:

  • Root module providers → Module expectations

Using Providers Inside the Module

resource "aws_s3_bucket" "primary" {
  provider = aws.primary
  bucket   = "${var.app_name}-primary"
}

resource "aws_s3_bucket" "replica" {
  provider = aws.replica
  bucket   = "${var.app_name}-replica"
}

Each resource is pinned to a specific provider instance.


Why Modules Must Not Define Their Own Providers

If a module defines its own providers:

  • It hardcodes regions and credentials

  • It becomes non-reusable

  • It prevents multi-environment deployments

Instead, modules should:

  • Declare what providers they need

  • Let the root module decide how those providers are configured

This is a core Terraform design principle.


Quick Start: Running Containers with the Docker Provider

Before jumping into Kubernetes, Terraform can manage containers locally using the Docker provider.

terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "~> 3.0"
    }
  }
}

provider "docker" {}

resource "docker_image" "nginx" {
  name = "nginx:latest"
}

resource "docker_container" "nginx" {
  image = docker_image.nginx.image_id
  name  = "terraform-nginx"

  ports {
    internal = 80
    external = 8080
  }
}

After running:

terraform apply

Nginx becomes available at:

👉 http://localhost:8080

This is a fast way to validate container behavior before deploying to a cloud platform.


From Containers to Kubernetes: Deploying on Amazon EKS

Once you understand container basics, the next step is orchestration. Terraform integrates directly with Kubernetes through providers.

Step 1: Provision an EKS Cluster

Using the official AWS module:

module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "~> 20.0"

  cluster_name    = "terraform-challenge-cluster"
  cluster_version = "1.29"

  vpc_id     = module.vpc.vpc_id
  subnet_ids = module.vpc.private_subnets

  eks_managed_node_groups = {
    default = {
      desired_size   = 2
      instance_types = ["t3.small"]
    }
  }
}

This provisions:

  • Managed Kubernetes control plane

  • Worker nodes

  • Networking infrastructure


Step 2: Configure the Kubernetes Provider

provider "kubernetes" {
  host                   = module.eks.cluster_endpoint
  cluster_ca_certificate = base64decode(
    module.eks.cluster_certificate_authority_data
  )

  exec {
    command = "aws"
    args    = ["eks", "get-token", "--cluster-name", module.eks.cluster_name]
  }
}

How Authentication Works

  • Terraform executes: aws eks get-token

  • AWS returns a temporary authentication token

  • The Kubernetes provider uses this token to authenticate

This approach:

  • Avoids static credentials

  • Leverages IAM securely


Step 3: Deploy a Kubernetes Workload

resource "kubernetes_deployment" "nginx" {
  metadata {
    name = "nginx-deployment"
  }

  spec {
    replicas = 2

    selector {
      match_labels = {
        app = "nginx"
      }
    }

    template {
      metadata {
        labels = {
          app = "nginx"
        }
      }

      spec {
        container {
          name  = "nginx"
          image = "nginx:latest"

          port {
            container_port = 80
          }
        }
      }
    }
  }
}

After deployment:

kubectl get pods

You should see running pods across your cluster.


How Terraform Connects Everything

Terraform builds a dependency graph:

  1. EKS cluster is created

  2. Outputs (endpoint, certificate) are exposed

  3. Kubernetes provider consumes these outputs

  4. Kubernetes resources deploy into the cluster

This seamless chaining is one of Terraform’s most powerful features.


Cost Considerations

Running an EKS cluster is not free:

  • Control plane: ~$0.10/hour

  • EC2 nodes: variable (~$4–6/day)

  • Networking and storage costs

👉 Approximate daily cost: $7–12

Always run:

terraform destroy

after testing to avoid unnecessary charges.


Key Takeaways

  • Provider aliasing enables multi-region and multi-account deployments

  • configuration_aliases defines expected provider inputs for modules

  • The providers map wires root configurations into modules explicitly

  • Terraform can manage everything from local containers → Kubernetes clusters

  • EKS + Kubernetes integration showcases Terraform’s full orchestration power


Final Thoughts

Today’s workflow demonstrated a complete progression:

Local Docker → Multi-provider Terraform modules → Kubernetes on AWS

This is the foundation of modern infrastructure engineering:

  • Modular

  • Environment-agnostic

  • Fully automated

Mastering provider patterns is what unlocks Terraform at scale.


1 views

More from this blog

T

Terraform Tales

27 posts