# Deploying Multi-Cloud Infrastructure with Terraform Modules

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**

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

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

These represent two distinct AWS environments.

* * *

### **Module Definition**

```hcl
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**

```hcl
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**

```hcl
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.

```hcl
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:

```bash
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:

```hcl
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**

```hcl
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**

```hcl
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:

```bash
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:

```bash
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.

* * *
