ad4469dc-7beb-4b7f-90b1-7de.../docs/05_modules_reuse.md

136 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 5) Build & Reuse a Simple Module (25 min)
In this section, youll turn repeated EC2 logic into a **reusable Terraform module**.
Modules make your code **DRY** (Dont Repeat Yourself), easier to test, and simpler to use across teams and environments.
## What is a module?
A **module** is just a folder that contains Terraform configuration files (`.tf`).
Your **root module** (the folder where you run the CLI) can **call** other modules from:
- A local path (`./modules/ec2-instance`)
- A Git repo (`git::https://...`), or
- A registry (`registry.terraform.io`)
Here well use a **local** module.
## Step 1: Create the project & module folder
```bash
mkdir -p ~/terraform-modules-lab/modules/ec2-instance
cd ~/terraform-modules-lab
```
- Creates a workspace with a nested folder `modules/ec2-instance` that will contain reusable EC2 code.
Your tree will look like:
```
terraform-modules-lab/
main.tf # root (calls the module)
modules/
ec2-instance/
variables.tf
main.tf # module implementation
outputs.tf
```
## Step 2: Define module inputs (variables)
Create **modules/ec2-instance/variables.tf**:
```hcl
variable "instance_type" { description = "Type of EC2 instance"; default = "t2.micro" }
variable "instance_name" { description = "Tag name for instance" }
variable "instance_count" { description = "Number of EC2"; default = 1 }
```
- These are **inputs** the module expects.
- `default` makes inputs optional (callers can override).
- You can add stronger typing & validation for safety:
```hcl
variable "instance_type" {
type = string
description = "EC2 size"
default = "t2.micro"
validation {
condition = can(regex("^t[23]\.", var.instance_type))
error_message = "Use a t2.* or t3.* instance for this lab."
}
}
```
## Step 3: Implement the module logic
Create **modules/ec2-instance/main.tf**:
```hcl
resource "aws_instance" "this" {
count = var.instance_count
ami = "ami-0e6329e222e662a52"
instance_type = var.instance_type
tags = { Name = "${var.instance_name}-${count.index}" }
}
```
- Uses `count` to create **N instances** with a single block.
- `tags.Name` includes the `count.index` suffix so each instance has a unique name (e.g., `App-Server-0`, `App-Server-1`).
> **Note on AMIs:** AMI IDs are **region-specific**. We use an Amazon Linux 2 AMI for **ap-south-1 (Mumbai)**. If you switch regions, update this AMI or fetch it dynamically (e.g., with a data source).
## Step 4: Expose useful outputs
Create **modules/ec2-instance/outputs.tf**:
```hcl
output "instance_ids" { value = [for i in aws_instance.this : i.id] }
output "public_ips" { value = [for i in aws_instance.this : i.public_ip] }
```
- Makes it easy for callers to **consume** important info (IDs, IPs).
- The `for` expression collects values from the resource instances created via `count`.
## Step 5: Call the module from the root
Create the **root** `main.tf` at `~/terraform-modules-lab/main.tf`:
```hcl
provider "aws" { region = "ap-south-1" }
module "ec2_instance" {
source = "./modules/ec2-instance"
instance_type = "t2.micro"
instance_name = "App-Server"
instance_count = 2
}
```
- `source` points to the **local path** of your module folder.
- Inputs (`instance_type`, `instance_name`, `instance_count`) are set by the **caller** (the root module).
- You can define **multiple** module blocks to create different groups of instances, or use `.tfvars` files for environments.
## Step 6: Initialize and apply
```bash
terraform init
terraform apply -auto-approve
```
- `terraform init` downloads providers and **fetches module sources** (local/Git/registry).
- `terraform apply` creates **2 EC2 instances** named `App-Server-0` and `App-Server-1` in **ap-south-1**.
After the apply, view outputs:
```bash
terraform output
```
You should see lists for `instance_ids` and `public_ips`.
## How to reuse this module elsewhere
- Copy the `modules/ec2-instance` folder into any Terraform project and call it with `source = "./modules/ec2-instance"`.
- Or publish the module to a **Git repo** and reference with `source = "git::https://github.com/yourorg/yourrepo//modules/ec2-instance?ref=v1.0.0"`.
- Standardize inputs/outputs so teams can use it without reading internals.
## Best practices
- **Version control your modules** (Git tags) to avoid breaking changes.
- Add **README.md** inside the module with usage examples and input/output docs.
- Prefer **types & validation** for variables.
- Keep AMIs **parametrized** or discover them via a `data "aws_ami"` query to avoid hard-coding.
- If multiple teams use the same module, consider a **private Terraform registry** or a Git monorepo with clear versioning.
## Cleanup
When done (to avoid charges):
```bash
terraform destroy -auto-approve
```
## Summary
You built a **reusable EC2 module**, exposed clean **inputs/outputs**, and consumed it from the **root**.
This pattern scales to VPCs, RDS, ALBs, and more—compose modules like building blocks to create reliable infrastructure at speed.