LAB 47 ‐ Advance modular approach to create instance - DevWithNehaCloud/Oracle-Cloud-Infrastructure-wiki GitHub Wiki

Infrastructure as Code (IaC) Migration: Static Modules to Dynamic for_each Approach

This document outlines the transition of our Compute Infrastructure provisioning from a Static Multi-Module Approach to an Advanced Dynamic Modular Approach using Terraform maps and for_each loops.

  1. Old Architecture vs. New Architecture 📂 Old Structure (Static/Manual Approach) In our previous setup, if we wanted to deploy multiple instances, we had to declare separate module blocks for each instance.

📂 Old Structure (Static/Manual Approach)

compute_service/
├── dev/
│   ├── instance.tf
│   ├── priv_key.pem
│   ├── provider.tf
│   ├── terraform.tfstate
│   └── variables.tf
└── instance_module/
    ├── main.tf
    └── variables.tf

📂 New Structure (Advanced Dynamic Approach) Now, we have centralized our configuration data inside a .tfvars file and use a single module block driven by a loop.

compute_service/
├── dev/
│   ├── instance.tf
│   ├── terraform.tfvars
│   ├── variables.tf
│   ├── priv_key.pem
│   ├── provider.tf
│   └── terraform.tfstate
└── instance_module/
    ├── main.tf
    └── variables.tf

📌

2. Key Code Changes

A. The Input Data (dev/terraform.tfvars)

Instead of writing Terraform code for every new server, we now just add a new block of data (configuration) inside this file.

instance_map={
  dev-instance-01 = {
    dev_display_name = "dev-app-server"
    dev_memory_in_gbs = "4"
    dev_ocpus = "1"
    dev_shape = "VM.Standard.E4.Flex"
    dev_boot_volume_size=50
    dev_Swiggy_Finance="dev"
    }
 dev-instance-02 = {
    dev_display_name = "dev-web-server"
    dev_memory_in_gbs= "4"
    dev_ocpus="1"
    dev_shape = "VM.Standard.E4.Flex"
    dev_boot_volume_size=50
    dev_Swiggy_Finance="dev"
    }
 dev-instance-03 = {
    dev_display_name = "dev-db-server "
    dev_memory_in_gbs= "8"
    dev_ocpus="1"
    dev_shape = "VM.Standard.E4.Flex"
   dev_boot_volume_size=50
    dev_Swiggy_Finance="dev"
    }
 }

What does terraform.tfvars do? It acts as a pure data sheet. It doesn't contain logic; it only feeds values (like name, shape, memory) into the variables defined in Terraform.

B. The Variable Definition (dev/variables.tf)

We defined a complex variable type (map(object)) to accept the structured data from the .tfvars file.

variable "instance_map" {
type=map(object({
    dev_display_name=string
    dev_memory_in_gbs=string
    dev_ocpus=string
    dev_shape=string
    dev_boot_volume_size=number
    dev_Swiggy_Finance=string
       }
      ))
  }



C. The Dynamic Loop (dev/instance.tf)

Instead of duplicating module blocks, we use a single block that automatically scales based on the data provided in terraform.tfvars.

module "dev_instance_creation" {
source = "/home/ngsoftlook/compute_service/instance_module"
for_each           =        var.instance_map
# Pass data dynamically using each.value
env_display_name   =        each.value.dev_display_name
env_memory_in_gbs  =        each.value.dev_memory_in_gbs
env_ocpus          =        each.value.dev_ocpus
env_shape=                  each.value.dev_shape
env_Swiggy_Finance  =       each.value.dev_Swiggy_Finance
env_boot_volume_size =      each.value.dev_boot_volume_size
}

D.(dev/provider.tf)

provider "oci" {
  auth             = "APIKey"
  tenancy_ocid     = "ocid1.tenancy.oc1..aaaaaaaaacrxza63ajbfdhvukzuxpv4y3oksxh256migpqx6lazpbbejuuga"
  user_ocid        = "ocid1.user.oc1..aaaaaaaaa5m4rn7uj5exypqxsz54tmkqctcuzd7o4ehy2f35flcdzznncw77a"
  private_key_path = "/home/ngsoftlook/privkey.pem"
  fingerprint      = "4c:bb:0e:8b:6e:b1:df:51:59:bf:c6:e3:60:2c:d6:29"
  region           = "us-ashburn-1"
}

📌 structure of the code under instance_module folder

main.tf

variables.tf

Now run the command

terraform init
terraform plan
terraform apply

Now, verifying in console

3. Why This Change? (Old vs. New Comparison)

Feature Old Approach (Static Modules) New Approach (Dynamic for_each)
Adding a New Instance Had to copy-paste the entire module block in instance.tf. Just add a few lines of configuration in terraform.tfvars.
Code Duplication High. instance.tf grew longer and messy with every new server. Minimal and Clean. The configuration file size depends on infrastructure features, not on the number of instances you deploy.
Performance Slower. Terraform had to evaluate multiple distinct module graphs. Much Faster. Terraform treats it as a single resource map.
Commands & Workflow Often required running terraform init again if module paths/configs shifted drastically. Plans were heavier. Standard Workflow. Direct terraform plan and apply work seamlessly. No redundant inits.

Destroy the instances one by one in terraform

terraform destroy -target="module.dev_instance_creation["dev-instance-03"]"

Command Executed:

terraform destroy -target="module.dev_instance_creation["dev-instance-03"]"

Error Message Received:

╷
│ Error: Invalid target "module.dev_instance_creation[dev-instance-03]"
│ 
│ Index brackets must contain either a literal number or a literal string.
╵

Troubleshooting: Terraform Invalid Target Error with for_each

📌 Issue Description

When attempting to destroy a specific instance created via a modular approach using the for_each meta-argument, passing the target string entirely within double quotes causes a syntax parsing failure in the terminal.

🔍 Root Cause Analysis

The issue occurs due to quote nesting conflicts within the Linux/Bash terminal:

The entire target path was wrapped in outer double quotes ("...").

Inside the square brackets, the specific instance key ("dev-instance-03") also used double quotes.

This causes the terminal to prematurely close the first string at the second quote mark, breaking the syntax before it ever reaches Terraform. As a result, Terraform strips the internal quotes and throws a literal string error.

🛠️ Solution

Solution: Use Outer Single Quotes ('...') — Recommended

The cleanest way to fix this in Linux/Bash is to wrap the entire target string in single quotes. This allows the terminal to pass the internal double quotes to Terraform exactly as written without escaping.

terraform destroy -target='module.dev_instance_creation["dev-instance-03"]'

OR destroy all the instances at once

terraform destroy

verifying in console

4. Summary of Benefits

  1. DRY (Don't Repeat Yourself): The infrastructure code is decoupled from the data. Engineers only touch the data file (.tfvars) to scale infrastructure.

  2. Blast Radius Reduction: Managing infrastructure as a map makes it easier for Terraform to identify exactly which instance is being added, updated, or destroyed without affecting others.

  3. Easier Maintenance: Adding or removing an instance is now just a matter of adding or deleting a few lines of configuration in the terraform.tfvars file, without changing the core infrastructure code.