LAB 50 ‐ create VCN, 1 subnet using terraform - DevWithNehaCloud/Oracle-Cloud-Infrastructure-wiki GitHub Wiki

Swiggy Dev Spoke Network Planning

Requirement Analysis

The objective of this activity is to design and deploy a Spoke Network in OCI using the Hub-and-Spoke Architecture Model. The Hub VCN is already available, and a new Spoke VCN needs to be created to host application workloads in a secure and scalable manner.

Component Requirement
Architecture Implement a Hub-and-Spoke Network Model in OCI
Existing Setup Hub VCN already exists and is managed separately
New Requirement Create a dedicated Spoke VCN for the Dev environment
VCN Type Private application network (Spoke Network)
Compartment Swiggy-Dev-Network
VCN Name spoke-dev-vcn
VCN CIDR 10.0.1.0/24
CIDR Planning Constraint CIDR must not overlap with the Hub VCN CIDR
Reason for CIDR Selection Hub VCN already uses 10.0.0.0/24; therefore, Spoke VCN uses 10.0.1.0/24 to avoid overlapping and routing conflicts
Subnet Type Private Application Subnet
Subnet Name app-subnet
Subnet CIDR 10.0.1.0/26
Current Capacity Requirement Support approximately 60 application instances
Future Planning Reserve IP space for two future subnets (30 IPs and 15–20 IPs respectively)
Route Table Create Route Table but keep Route Rules empty for now
Gateway Requirement No Internet Gateway, NAT Gateway, Service Gateway, or DRG at this stage
Security Requirement Allow SSH (TCP Port 22) for testing and administration
Ingress Rule Allow SSH (TCP 22) only from trusted internal networks: Hub VCN (10.0.0.0/24) and Spoke VCN (10.0.1.0/24)
Egress Rule Default outbound access
Tagging Requirement Use Defined Tags for governance and cost tracking
Naming Standard Follow meaningful and consistent naming conventions
Deployment Method Terraform Advanced Modular Approach
Terraform Design Reusable Module + Environment-specific Configuration
Scalability Goal Network should support future subnet expansion without redesign

Expected Outcome

At the end of the implementation, the following OCI resources should be created:

  • One Spoke VCN (spoke-dev-vcn)
  • One Private Application Subnet (app-subnet)
  • One Route Table (without Route Rules)
  • One Security List (with SSH access for testing)
  • Defined Tags for resource governance and cost tracking
  • Modular Terraform code that can be reused across multiple environments

Objective

Before implementing the Hub-and-Spoke architecture using Terraform, proper network planning was performed to ensure:

  • No CIDR overlap between Hub and Spoke networks
  • Future scalability
  • Clean IP address management
  • Industry-standard network design
  • Easy troubleshooting and routing

Initial Requirement

The requirement was to create a new Spoke VCN for application workloads.

IP Requirements

Network Segment Required IPs
App Subnet 60
Future Subnet 1 30
Future Subnet 2 15-20
Total ~110

Existing Hub Network

The Hub VCN was already created.

Hub VCN
CIDR : 10.0.0.0/24

Hub Subnet Design

Hub VCN
10.0.0.0/24

256 IPs
│
├── Public Subnet
│   10.0.0.0/25
│   128 IPs
│
└── Private Subnet
    10.0.0.128/25
    128 IPs

Enterprise Address Space (Umbrella CIDR)

The organization reserved:

10.0.0.0/16

This serves as the enterprise address space from which all future VCNs will be allocated.

10.0.0.0/16
│
├── Hub VCN
├── Dev VCN
├── Test VCN
├── UAT VCN
├── Prod VCN
└── Future VCNs

Why We Did NOT Use 10.0.0.0/24 Again

Initially it might appear logical to use:

Spoke VCN = 10.0.0.0/24

However, the Hub VCN is already using:

10.0.0.0/24

This creates CIDR overlap.


CIDR Overlap Example

Hub

10.0.0.50

Spoke

10.0.0.50

Now both networks can contain the same IP address.

The router cannot uniquely identify the destination network.

This results in:

  • Routing ambiguity
  • Communication failures
  • IP conflicts
  • Hub-Spoke connectivity issues

This condition is known as:

CIDR Overlapping

Industry Rule

Hub and Spoke VCNs must never have overlapping CIDR ranges.

Incorrect

Hub VCN   = 10.0.0.0/24

Spoke VCN = 10.0.0.0/24

❌ CIDR Overlap


Correct

Hub VCN   = 10.0.0.0/24

Spoke VCN = 10.0.1.0/24

✅ No Overlap


Why We Selected 10.0.1.0/24

The Hub VCN already consumed:

10.0.0.0/24

The next logical non-overlapping network block inside the enterprise CIDR space is:

10.0.1.0/24

This keeps the addressing plan clean and scalable.


Final VCN Design

Enterprise CIDR
10.0.0.0/16
│
├── Hub VCN
│   10.0.0.0/24
│
├── Dev Spoke VCN
│   10.0.1.0/24
│
├── Test Spoke VCN
│   10.0.2.0/24
│
├── UAT Spoke VCN
│   10.0.3.0/24
│
└── Prod Spoke VCN
    10.0.4.0/24

Why We Chose /24

The total requirement was approximately:

60 + 30 + 20 = 110 IPs

Available CIDR options:

CIDR Total IPs
/25 128
/24 256
/23 512

/25

128 IPs

Possible, but leaves very little room for future expansion.


/23

512 IPs

Too large for the current requirement.


/24

256 IPs

Provides sufficient space while keeping the network manageable.

Therefore:

10.0.1.0/24

was selected.


Subnet Planning

App Subnet

Requirement:

60 IPs

CIDR options:

CIDR IPs
/27 32
/26 64

Selected:

10.0.1.0/26

Provides:

64 IPs

Future Subnet 1

Requirement:

30 IPs

Selected:

10.0.1.64/27

Provides:

32 IPs

Future Subnet 2

Requirement:

15-20 IPs

Possible options:

Option A

/28

Provides:

16 IPs

Risk of running out of addresses.


Option B

/27

Provides:

32 IPs

Offers future growth.

Selected:

10.0.1.96/27

Final Network Layout

Dev Spoke VCN
10.0.1.0/24

256 IPs
│
├── App Subnet
│   10.0.1.0/26
│   64 IPs
│
├── Future Subnet 1
│   10.0.1.64/27
│   32 IPs
│
├── Future Subnet 2
│   10.0.1.96/27
│   32 IPs
│
└── Reserved For Future Growth
    10.0.1.128 - 10.0.1.255

Design Justification

Why did I choose 10.0.1.0/24 for the Spoke VCN?

The Hub VCN was already using 10.0.0.0/24. To avoid CIDR overlap and maintain a clean addressing scheme within the enterprise address space (10.0.0.0/16), I selected the next available non-overlapping block, 10.0.1.0/24, for the Dev Spoke VCN. The /24 range provides sufficient address capacity for the current application subnet requirements while also allowing future subnet expansion.


Key Takeaways

  • Never create overlapping VCN CIDRs.
  • Always plan VCNs from a larger enterprise CIDR block.
  • Use sequential CIDR allocation for easier management.
  • Size subnets based on current and future requirements.
  • Leave sufficient address space for future growth.
  • CIDR planning should be completed before Terraform implementation.

Enterprise Address Space

The organization reserved the CIDR block 10.0.0.0/16 for future network expansion.

All Hub and Spoke VCNs are allocated from this address space.

Where is 10.0.0.0/16 configured?

It is not configured as a resource in OCI. It is part of the network planning and address management strategy. Individual VCN CIDRs such as 10.0.0.0/24 and 10.0.1.0/24 are carved out from the planned enterprise address space 10.0.0.0/16.

Network Planning Methodology

Before implementing any cloud network, it is important to follow a structured planning approach to ensure scalability, security, and efficient resource utilization.

1. Gather Requirements and Constraints

  • Understand business, application, security, and connectivity requirements.
  • Identify organizational standards, compliance requirements, and existing network dependencies.
  • Determine current and future growth expectations.

2. Assess Available IP Address Space

  • Review the organization's existing IP addressing strategy.
  • Identify available address ranges for cloud deployment.
  • Ensure sufficient address space is reserved for future workloads and environments.

3. Select an Appropriate VCN CIDR Block

  • Choose a CIDR range that does not overlap with existing on-premises networks, other cloud environments, or future planned networks.
  • Consider long-term expansion requirements when selecting the CIDR block.

4. Design Subnets Using VLSM

VLSM (Variable Length Subnet Masking) allows network administrators to create subnets of different sizes based on actual host requirements rather than allocating the same subnet size everywhere.

Benefits of VLSM

  • Optimizes IP address utilization.
  • Reduces IP address wastage.
  • Allows subnet sizing based on workload requirements.
  • Preserves address space for future expansion.
  • Improves overall network scalability.

Example

Subnet Required Hosts CIDR Available IPs
App Subnet 60 /26 64
DB Subnet 30 /27 32
Management Subnet 15 /28 16

Instead of allocating equal-sized subnets, VLSM assigns subnet sizes according to actual requirements, resulting in more efficient IP utilization.

5. Allocate Subnet CIDRs Strategically

  • Assign CIDR ranges to public and private subnets.
  • Keep sufficient unallocated space for future subnet creation.
  • Reserve address space for future applications, environments, and growth.

6. Plan Network Components

Design and configure the required networking resources:

  • Route Tables
  • Security Lists
  • Network Security Groups (NSGs)
  • Internet Gateway (IGW)
  • NAT Gateway
  • Service Gateway
  • Dynamic Routing Gateway (DRG)
  • Load Balancers (if required)

Define traffic flow and security boundaries between network segments.

7. Validate the Network Design

Before implementation, verify that:

  • No CIDR overlap exists.
  • IP addresses are efficiently utilized.
  • Routing paths are properly configured.
  • Security controls meet requirements.
  • The design supports scalability and high availability.
  • Future expansion can be accommodated without major redesign.

Key Objective

A planning-first approach helps create a secure, scalable, and maintainable cloud network architecture. Proper upfront design minimizes future network rework, prevents IP addressing conflicts, optimizes IP address utilization through VLSM, and aligns with enterprise cloud networking best practices.

Terraform Structure

Terraform Directory Structure

Swiggy_Infrastructure
├── dev
│   ├── provider.tf
│   ├── versions.tf
│   ├── network.tf
│   ├── variables.tf
│   ├── terraform.tfvars
│   └── outputs.tf
│
└── network_module
    ├── main.tf
    ├── variables.tf
    └── outputs.tf

Directory Purpose

Directory/File Purpose
dev Environment-specific configuration
provider.tf OCI Provider configuration
versions.tf Terraform and provider version constraints
network.tf Module invocation using for_each
variables.tf Input variable definitions
terraform.tfvars Environment-specific values
outputs.tf Module outputs
network_module/main.tf Creates VCN, Route Table, Security List, and Subnet
network_module/variables.tf Module input variables
network_module/outputs.tf Module output values

dev/provider.tf

provider "oci" {

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

dev/variables.tf


variable "compartment_id" {
  type = string
}

variable "network_map" {

  type = map(object({

    vcn_name            = string
    vcn_cidr            = string

    subnet_name         = string
    subnet_cidr         = string

    route_table_name    = string
    security_list_name  = string
    ssh_source_1 = string
    ssh_source_2 = string

  }))
}

dev/terraform.tfvars

compartment_id = "ocid1.compartment.xxxxxxxxxxxxxxxxxxxxxxxxx"

network_map = {

  spoke-app-network = {

    vcn_name           = "spoke-dev-vcn"

    vcn_cidr           = "10.0.0.0/24"

    subnet_name        = "spoke-dev-app-subnet"

    subnet_cidr        = "10.0.0.0/26"

    route_table_name   = "spoke-dev-route-table"

    security_list_name = "spoke-dev-security-list"

    ssh_source_1 = "10.0.0.0/24"

    ssh_source_2 = "10.0.1.0/24"

  }

}

dev/network.tf

module "network_module" {

  for_each = var.network_map

  source = "../network_module"

  compartment_id = var.compartment_id

  vcn_name = each.value.vcn_name

  vcn_cidr = each.value.vcn_cidr

  subnet_name = each.value.subnet_name

  subnet_cidr = each.value.subnet_cidr

  route_table_name = each.value.route_table_name

  security_list_name = each.value.security_list_name
        ssh_source_1 = each.value.ssh_source_1

        ssh_source_2 = each.value.ssh_source_2

}

dev/outputs.tf

output "network_details" {

  value = {

    for k, v in module.network_module :

    k => {

      vcn_id           = v.vcn_id
      subnet_id        = v.subnet_id
      route_table_id   = v.route_table_id
      security_list_id = v.security_list_id

    }

  }

}

network_module/variables.tf


variable "compartment_id" {
  type = string
}

variable "vcn_name" {
  type = string
}

variable "vcn_cidr" {
  type = string
}

variable "subnet_name" {
  type = string
}

variable "subnet_cidr" {
  type = string
}

variable "route_table_name" {
  type = string
}

variable "security_list_name" {
  type = string
}

variable "ssh_source_1" {
  description = "Hub VCN CIDR"
  type        = string
}

variable "ssh_source_2" {
  description = "Spoke VCN CIDR"
  type        = string
}

network_module/main.tf

VCN


resource "oci_core_vcn" "spoke_vcn" {

  compartment_id = var.compartment_id

  cidr_blocks = [
    var.vcn_cidr
  ]

  display_name = var.vcn_name

  dns_label = "spokedev"
}

Route Table with no route rule

resource "oci_core_route_table" "spoke_rt" {

  compartment_id = var.compartment_id

  vcn_id = oci_core_vcn.spoke_vcn.id

  display_name = var.route_table_name

  #route_rules = []
}

Security List

resource "oci_core_security_list" "spoke_sl" {

  compartment_id = var.compartment_id

  vcn_id = oci_core_vcn.spoke_vcn.id

  display_name = var.security_list_name

  ingress_security_rules {

  protocol = "6"

  source = var.ssh_source_1

  tcp_options {
    min = 22
    max = 22
  }
}

ingress_security_rules {

  protocol = "6"

  source = var.ssh_source_2

  tcp_options {
    min = 22
    max = 22
  }
}

  egress_security_rules {

    protocol = "all"

    destination = "0.0.0.0/0"

  }
}

Private App Subnet


resource "oci_core_subnet" "app_subnet" {

  compartment_id = var.compartment_id

  vcn_id = oci_core_vcn.spoke_vcn.id

  cidr_block = var.subnet_cidr

  display_name = var.subnet_name

  prohibit_public_ip_on_vnic = true

  route_table_id = oci_core_route_table.spoke_rt.id

  security_list_ids = [
    oci_core_security_list.spoke_sl.id
  ]

  dns_label = "appsubnet"
}

network_module/outputs.tf

output "vcn_id" {
  value = oci_core_vcn.spoke_vcn.id
}

output "subnet_id" {
  value = oci_core_subnet.app_subnet.id
}

output "route_table_id" {
  value = oci_core_route_table.spoke_rt.id
}

output "security_list_id" {
  value = oci_core_security_list.spoke_sl.id
}

Terraform will create

Swiggy-Dev-Network
│
├── spoke-dev-vcn
│   CIDR: 10.0.0.0/24
│
├── spoke-dev-app-subnet
│   CIDR: 10.0.0.0/26
│
├── spoke-dev-route-table
│   Empty
│
└── spoke-dev-security-list
    TCP 22 Open

If tomorrow we need to create 5 more Spoke VCNs So without modifying the code, you'll just add a new entry to terraform.tfvars

Now run the commands

1. Format

terraform fmt -recursive

2. Initialize

terraform init

3. validate

terraform validate

Expected: Success! The configuration is valid.

4. Plan

terraform plan

If everything is good then output should be

Plan: 4 to add, 0 to change, 0 to destroy.

5. Apply

terraform apply

verifying in Console

Terraform Formatting Commands

terraform fmt

Formats Terraform configuration files only in the current directory.

terraform fmt -recursive

Formats Terraform configuration files in the current directory and all nested subdirectories.

Why Use -recursive?

The -recursive option is commonly used in modular Terraform projects where code is distributed across multiple folders and modules. It ensures consistent formatting throughout the entire Terraform codebase with a single command.

Example

terraform fmt

Formats files only in the current folder.

terraform fmt -recursive

Formats files in the current folder and all subfolders.

Interview Answer

terraform fmt formats Terraform configuration files only in the current directory, whereas terraform fmt -recursive formats Terraform files in the current directory as well as all nested subdirectories. The recursive option is commonly used in modular Terraform projects to maintain consistent formatting across the entire codebase.

Tagging Strategy

Objective

After provisioning the complete Hub-and-Spoke network infrastructure using Terraform Advanced Modular Approach, Defined Tags were implemented to support:

  • Cost Tracking
  • Resource Governance
  • Resource Identification
  • Standardized Resource Management

Defined Tag Namespace

Namespace:

CloudOps

Defined Tags:

Tag Key Value
Environment Dev
Project Swiggy

Why Defined Tags?

Defined Tags were chosen instead of Free-Form Tags because:

  • Centrally managed and governed
  • Enforces standardization
  • Supports cost tracking and reporting
  • Prevents inconsistent tag values
  • Recommended for enterprise environments

Tag Namespace and Tag Definition Creation

Before implementing tags through Terraform, a Defined Tag Namespace and Tag Definitions were created in OCI Console.

A tag namespace named CloudOps was created, followed by the creation of defined tag keys such as Environment and Project. These tags were later referenced in Terraform and applied consistently across all network resources for governance and cost tracking purposes.

Terraform Tagging Design

A common tagging strategy was implemented using Terraform Locals.

locals.tf

locals {
  common_tags = {
    "CloudOps.Environment" = "Dev"
    "CloudOps.Project"     = "Swiggy"
  }
}

Note:- No Tag Namespace OCID or Tag Definition OCID was required during the Terraform implementation. Terraform automatically recognized the existing Defined Tag Namespace (CloudOps) and Tag Keys (Environment, Project) in OCI and applied them to the resources using the configured defined_tags block.

Why Locals?

Without locals, the same tags would need to be defined repeatedly for every resource.

Using locals provides:

  • Centralized tag management
  • Reusable configuration
  • Reduced code duplication
  • Easier maintenance

Tags are defined once and reused across all resources.


Resources Tagged

The following OCI resources were tagged:

Resource
VCN
Subnet
Route Table
Security List

Implementation:

defined_tags = var.defined_tags

Now attaching tag to all 4 resources VCN,RT,SL,Subnet

Module Integration

Tags were passed from the environment layer to the reusable network module.

Flow:

locals.tf
    ↓
network.tf
    ↓
network_module
    ↓
OCI Resources

Result

All network resources were successfully tagged with:

CloudOps.Environment = Dev
CloudOps.Project     = Swiggy

Tags have been successfully attached to all four resources. Below is the screenshot for the VCN. Similar tags can also be verified on the Route Table, Security List, and Subnet resources.

This enables:

  • Cost allocation by project
  • Environment-wise resource identification
  • Consistent governance across infrastructure
  • Future scalability for Test, UAT, and Prod environments

Cost Tracking Strategy

Defined Tags are used to categorize resources for cost analysis.

Current Tags:

CloudOps.Environment = Dev
CloudOps.Project     = Swiggy

Benefits:

  • Track project-specific spending
  • Filter resources by environment
  • Simplify cost reporting
  • Improve governance and accountability

Future environments such as Test, UAT, and Prod can reuse the same tagging framework.

Code Push to GitHub Repository

After completing the Terraform implementation, the entire project was pushed to the GitHub repository.

Before pushing the code, a .gitignore file was created to exclude sensitive files from version control. The following files were ignored:

touch .gitignore
vi .gitignore

copy and paste this

# Terraform State Files
*.tfstate
*.tfstate.*

# Sensitive Provider Configuration
provider.tf

# Key Information
privkey.pem

and then press esc:wq!

cat .gitignore

it will show all the files that you have ignored

  • provider.tf
  • *.tfstate and *.tfstate.*
  • privkey.pem

then run all the commands

git init
git add .
git status
git commit -m "Initial commit: Swiggy Infrastructure with Advanced Modular Terraform"
git branch -M main
git remote add origin <GitHub-Repository-URL>
git remote -v
git push -u origin main

Since the Git credential helper was already configured as store, the previously saved GitHub credentials were reused. Therefore, no additional username or Personal Access Token (PAT) was required during the push operation.

git config --global credential.helper

output should be store if the credentials are already stored

Finally, all Terraform modules, environment-specific configurations, and supporting files were successfully pushed to the GitHub repository.

Repository: https://github.com/DevWithNehaCloud/Swiggy_Infrastructure

Code Refactoring and Module Structure Enhancement

To improve code readability, maintainability, and alignment with industry standards, the network module was refactored by separating individual resources into dedicated Terraform files.

Updated Module Structure

network_module/
├── vcn.tf
├── route_table.tf
├── security_list.tf
├── subnet.tf
├── variables.tf
└── outputs.tf

Benefits

  • Improved code organization and readability
  • Easier troubleshooting and maintenance
  • Better scalability for future enhancements
  • Resource-specific configuration separated into dedicated files
  • Terraform state remained unchanged during refactoring

Validation

After refactoring, Terraform validation and plan execution were performed successfully.

terraform validate
terraform plan

Result:

No changes. Your infrastructure matches the configuration.

This confirms that only the code structure was improved and no OCI resources were modified or recreated.

Project Structure

Swiggy_Infrastructure
│
├── dev
│   ├── network.tf
│   ├── variables.tf
│   ├── locals.tf
│   └── terraform.tfvars
│
└── network_module
    ├── vcn.tf
    ├── route_table.tf
    ├── security_list.tf
    ├── subnet.tf
    ├── variables.tf
    └── outputs.tf

Folder Description

dev

Environment-specific configuration files:

  • network.tf → Calls the reusable network module.
  • variables.tf → Defines input variable structure.
  • locals.tf → Stores common values such as Defined Tags.
  • terraform.tfvars → Provides actual values for the Dev environment.

network_module

Reusable Terraform module containing OCI network resources:

  • vcn.tf → Creates the Spoke VCN.
  • route_table.tf → Creates the Route Table.
  • security_list.tf → Creates the Security List with ingress and egress rules.
  • subnet.tf → Creates the Private Application Subnet.
  • variables.tf → Defines module input variables.
  • outputs.tf → Exposes resource outputs for future use.

network_module/vcn.tf

resource "oci_core_vcn" "spoke_vcn" {

  compartment_id = var.compartment_id

  cidr_blocks = [
    var.vcn_cidr
  ]

  display_name = var.vcn_name

  dns_label    = "spokedev"

  defined_tags = var.defined_tags
}

network_module/route_table.tf

resource "oci_core_route_table" "spoke_rt" {

  compartment_id = var.compartment_id

  vcn_id = oci_core_vcn.spoke_vcn.id

  display_name = var.route_table_name

  defined_tags = var.defined_tags

  # As of now no route rules
}

network_module/security_list.tf

resource "oci_core_security_list" "spoke_sl" {

  compartment_id = var.compartment_id

  vcn_id = oci_core_vcn.spoke_vcn.id

  display_name = var.security_list_name

  defined_tags = var.defined_tags

  ingress_security_rules {

    protocol = "6"

    source = var.ssh_source_1

    tcp_options {
      min = 22
      max = 22
    }
  }

  ingress_security_rules {

    protocol = "6"

    source = var.ssh_source_2

    tcp_options {
      min = 22
      max = 22
    }
  }

  egress_security_rules {

    protocol    = "all"

    destination = "0.0.0.0/0"
  }
}

network_module/subnet_list.tf

resource "oci_core_subnet" "app_subnet" {

  compartment_id = var.compartment_id

  vcn_id = oci_core_vcn.spoke_vcn.id

  cidr_block = var.subnet_cidr

  display_name = var.subnet_name

  prohibit_public_ip_on_vnic = true

  route_table_id = oci_core_route_table.spoke_rt.id

  security_list_ids = [
    oci_core_security_list.spoke_sl.id
  ]

  dns_label = "appsubnet"

  defined_tags = var.defined_tags
}

network_module/main.tf

delete entire content and paste this
# Resources moved to individual files

Now run all the terraform commands like fmt recursive, validate, plan. No need to run terraform apply because there is no configuration changes. Finally push these new code in github repo by running the command like git add., commit,push.

Refactored the network module by separating VCN, Route Table, Security List, and Subnet resources into dedicated Terraform files. After refactoring, terraform plan returned "No changes. Your infrastructure matches the configuration.", confirming that the code structure was improved without modifying any OCI resources.

Initially all network resources were maintained in a single main.tf file. Later, for better maintainability and industry-standard code organization, the resources were separated into dedicated files such as vcn.tf, subnet.tf, route_table.tf, and security_list.tf. No infrastructure changes were introduced during this refactoring, which was validated using Terraform Plan showing no changes.