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.
- 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
-
DRY (Don't Repeat Yourself): The infrastructure code is decoupled from the data. Engineers only touch the data file (.tfvars) to scale infrastructure.
-
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.
-
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.