OCI Terraform Part 3 - Terraform: Loops, Modules, and Infrastructure Automation

Mastering Terraform: Leveraging Loops and Modules for Efficient Infrastructure Management

Terraform is a declarative language that doesn't have typical for-loops for iterating over a list of objects or creating repetitive resources. However, Terraform offers meta-parameters like count, for_each, and for expressions that achieve similar functionality.

Methods to Store Values

There are three types of methods to store values:

  • List → Ordered, indexed access, and allows duplicates.
  • Map → Unordered, key-based access, and unique key values.
  • Set → Unordered, no duplicates, easy to find the presence of the value.

Iterations: Loops

Count

The count parameter allows iteration over each resource provided in a list or module. The count.index helps in iterating over specific resources or looping through each resource.

Let's create three users with different names using a single resource block and count:

variable "usernames" {
  type = list(string)
  default = ["cms", "AM", "AC"]
}

resource "oci_identity_user" "user" {
  count = 3
  name = var.usernames[count.index]
  description = "OCI User"
}

output "username_name" {
  value = var.usernames[*]
}

This will create three users with the following output:

+ username_name = [
    + "cms",
    + "AM",
    + "AC",
]

for_each

The for_each loop allows iteration over lists and maps to create multiple copies of a resource or module. It supports only sets and maps on resources.

variable "usernames" {
  type = list(string)
  default = ["cms", "AM", "AC"]
}

resource "oci_identity_user" "user" {
  for_each = toset(var.usernames)
  name = each.value
  description = "OCI User"
}

output "username_name" {
  value = oci_identity_user.user
}

This code displays all the information related to the users created with the block of code. There are additional methods, such as conditional statements.

Using Modules to Create Infrastructure

Let's use the same concepts in modules to create our infrastructure. First, let's understand what Terraform modules are.

What Are Terraform Modules?

Terraform modules are essential building blocks that allow you to organize and reuse your infrastructure code. Instead of repeating the same code across different Terraform configurations, you can encapsulate it within a module and call that module wherever it's needed. This approach improves code maintainability, reduces errors, and enhances collaboration by creating standardized infrastructure components.

Modules help to keep your Terraform configurations DRY (Don't Repeat Yourself). For example, if you need to create multiple Virtual Cloud Networks (VCNs) or subnets with similar configurations, instead of duplicating the code, you can define the logic in a module and reuse it.

Why Are Modules Necessary?

  • Reusability: Modules allow you to reuse the same configuration across different environments, such as development, staging, and production.
  • Organization: By breaking down complex configurations into smaller, focused modules, you make your Terraform code easier to manage and understand.
  • Consistency: Using modules ensures that your infrastructure components are created consistently, reducing the likelihood of errors.
  • Scalability: As your infrastructure grows, modules help scale the management of resources by making it easier to add, modify, or remove components.

Example 1: Single VCN Creation Using a Module

Variable Definitions (variable.tf)

variable "compartment_id" {
  description = "Network compartment id"
  default = "<compartment-ocid>"
  type = string
}

variable "cidr_block" {
  description = "VCN CIDR block"
  type = list(string)
}

variable "display_name" {
  description = "Name of my VCN"
  type = list(string)
}

Explanation: In this block, we define variables that will be used in the VCN module. The compartment_id is the ID of the OCI compartment where the VCN will be created. The cidr_block variable is a list of CIDR blocks that the VCN will use, and the display_name is a list containing the name of the VCN.

Output Definitions (output.tf)

output "vcn_ids" {
  value = [for vcn in oci_core_vcn.vcn : vcn.id]
  description = "VCN ID of the VCN"
}

Explanation: This block outputs the ID of the VCN created by the module. The vcn_ids output is a list of IDs for all the VCNs created by this module.

Main Configuration (main.tf)

resource "oci_core_vcn" "vcn" {
  compartment_id = var.compartment_id
  for_each = {for idx, cidr in var.cidr_block : idx => cidr}
  cidr_block = each.value
  display_name = var.display_name[each.key]
}

Explanation: Here, we define the main logic for creating a VCN. The for_each loop iterates over the list of CIDR blocks, creating a VCN for each entry. The compartment_id is passed in from the variables, and the display_name is used to name the VCN.

Calling the Module in Main Configuration (vcn.tf)

module "vcn" {
  source = "../Dev/module_vcn"
  vcn_cidr = ["10.0.0.0/16"]
  display_name = ["cms_vcn1"]
}

Explanation: In this block, we call the VCN module from our main Terraform configuration. We specify the source of the module and provide the necessary inputs, such as the CIDR block and the display name. This call will create a single VCN based on the logic defined in the module.

Example 2: Creating Multiple Subnets in a Single VCN Using Modules

Variable Definitions (variable.tf)

variable "compartment_id" { 
 description = "subnet compartment id"
 default = "<compartment-ocid>"
 type = string
}

variable "cidr_block" {
  description = "Subnet CIDR block"
  type = list(string)
}

variable "vcn_ids" {
  description = "VCN ID of the subnet"
  type = list(string)
}

variable "display_name" {
  type = list(string)
  description = "Display name of the subnet"
}

variable "prohibit_public_ip_on_vnic" {
  description = "Whether VNICs in this subnet can have public IP addresses."
  type = bool
  default = false
}

Output Definitions (output.tf)

output "subnet_id" {
  value = [for subnet in oci_core_subnet.subnet : subnet.id]
  description = "Subnet ID of the Subnet"
}

Main Configuration (main.tf)

resource "oci_core_subnet" "subnet" {
  compartment_id = var.compartment_id
  for_each = {for idx, cidr in var.cidr_block : idx => cidr}
  vcn_id = var.vcn_ids[each.key]
  cidr_block = each.value
  prohibit_public_ip_on_vnic = var.prohibit_public_ip_on_vnic
  display_name = var.display_name[each.key]
}

Explanation: This block creates subnets within a VCN. The for_each parameter determines how many subnets to create based on the length of the cidr_block list. Each subnet is assigned a CIDR block, a VCN ID, a name, and a public/private designation based on the provided variables.

Calling the Module in Main Configuration (subnet.tf)

module "subnet" {
  source = "../module_subnet"
  vcn_ids = ["10.0.0.0/16"]
  display_name = ["cms_subnet1"]
  cidr_block = ["10.0.0.0/24"]
}

Explanation: In this block, we call the subnet module to create multiple subnets within a single VCN. The module uses the CIDR blocks, VCN IDs, display names, and public/private settings provided in the inputs to create each subnet.

Conclusion

Terraform's loops, if-statements, and modules offer powerful tools for managing cloud infrastructure efficiently. By mastering these concepts, one can create flexible, reusable configurations that scale with the needs.

No comments:

Post a Comment