Hi Everyone,
By the end of this article, hopefully , you would be able to understand the concepts of Modules in Terraform.
A Module is a reusable code component that is defined in Terraform and it can be invoked onto different projects as and when required.
Use Case:
Suppose the company has a requirement to use a specific instance type for whatever infrastructure is to be provisioned. Hence to enforce this, a module with the instance_type can be defined. Now , whenever a project requires to bring up an instance, they can just invoke the module , for which the specifications are already set. The added advantage is that, while invoking, the instance type CANNOT be overridden by the project user (ROOT module), unless the child module uses variable for that attribute.
Implementation
Repository Link : https://github.com/linubajy/Terraform
There are 2 ways to declare a module —
Method 1
Here, we are simply declaring modules that are available in the Public Registry. These modules are community based . There will be different versions for it, which optionally can be specified in our configuration. If its not specified, it will take the latest version of the specified module, which might be an issue as it might cause an issue in future when newer versions are released. It saves us the trouble creating module configurations.
terraform-public-registry-module.tf
---------------------------------------------------------------------------
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "<4.49.0" /* Terraform version, not to be confused with
module version */
}
}
}
provider "aws" {
region = "us-west-2"
access_key = "demo"
secret_key = "demo"
}
module "ec2-instance" {
source = "terraform-aws-modules/ec2-instance/aws"
version = "4.3.0" /* Public registry module version */
}
Here we use the “module” keyword ,having the local name “ec2-instance”. The source will be downloaded to the local during initialization phase. And the instance will be created after the apply command.
Method 2
Folder Structure for custom Modules
We have 2 different folders —
- Modules -which will have all the resources(here ec2) , under which it will have its associated files — main.tf ( Defining configurations for the resources ), outputs.tf and variables.tf (Self explanatory). The main.tf is called the child module.
- Projects — Here we invoke the custom modules using “module” keyword. This is the Parent module. We can have multiple project folders. Here I have named it as “Staging”
In the Child module folder (ec2) , we observe that they are similar to what we have been defining . We use terraform resource blocks and the providers.
In the Parent module, the syntax is similar to as we have seen before except that the source will now be the local path.
main.tf
----------------------------------------------------------------------------
provider "aws" {
region = "us-west-2"
access_key = "demo"
secret_key = "demo"
}
module "instance" {
source = "../Modules/ec2"
env = var.env
}
Based on my directory structure, I formulated an easy way to find the local path. The current file is main.tf . So the current directory is “Staging”. This should be our starting point. From there , the “../ ” takes us to the previous directory ie “Module-Example” directory. And from there, we can now navigate to Modules directory and then ec2.
Variables in Custom Modules
- The variables that we declare in Parent module , should be declared in Child Module (say here “env” variable is declared in both in parent and child module in variables.tf file)
- The variables can be initialized with a value in the “.tfvars file” in the Parent module. ( Here Staging.tfvars has a variable “env” that is initialized with value “Staging” ). This has to be passed during runtime.
Output in Custom Modules
- The Outputs from Child Module should be the the attribute name (here, “public_ip” )
- The output from Child module is then caught by the Parent module , which is in the format :
module.<local-name-of-module>.<output-name-from-child-module> . Here , it would be module.instance.public_ip.
- The output name in the Parent module can be customized. Here , I have used the name as “staging-instance-public-ip”. It doesn’t need to be an attribute name as observed in the Child Module.
output.tf from Staging directory [Parent Module]
----------------------------------------------------------------------------
output "staging-instance-public-ip" {
value = module.instance.public_ip
description = "Public IP of Staging instance"
}
Overriding parameters
Now that we have the instance_type in Child Module, what if the Parent Module wants to override this. Straightforward answer is we cant do anything from parent module. However, if the Child module decides to give the option of overriding, it can be done by variables.
Here, if we declare a variable called instance_type and specify a default value , then now the parent module can pass its value when using the module
In Module/ec2/ec2.tf (Child Dir)
-------------------------------------
resource "aws_instance" "myec2" {
ami = "ami-094125af156557ca2"
instance_type = var.instance-type
tags = {
name = "Environment"
value = var.env
}
}
In Module/ec2/variables.tf (Child Dir)
--------------------------------------------
########### USE A VARIABLE WITH DEFAULT VALUE TO OVERRIDE IN MODULE ##########
variable "instance-type" {
default = "t2.micro"
}
In Staging/main.tf (Parent Dir)
------------------------------
provider "aws" {
region = "us-west-2"
access_key = "demo"
secret_key = "demo"
}
// Declare and define all vars defined in tfvars file
module "instance" {
source = "../Modules/ec2"
instance_type = "t2.large"
env = var.env
}
Observations:
- File names are not case sensitive. Even though my file name is “staging.tfvars” , it accepted “Staging.tfvars” in the Prompt
- The plan, apply or destroy command, it needed the vars-file as the argument. If not provided, it will prompt up during runtime, requesting for user input for the variable .
By now, you have a clear idea of what modules are, and how it benefits us. Once you have your basics right, you can create any number of modules for various resource, and use it among various projects.