Concept of Terraform module and module scope

Concept of Terraform module and module scope

When I started with Terraform learning, I was mostly writing independent files for each of the resources which needs to be provisioned. After few days, I had left with so many terraform files, which need to be maintained. That is the time, I thought this is not it with terraform. I started exploring more and ended up with the concept of modules.

In simpler terms, when you code the application, you try to make it modular. In the same way, we can create our terraform files in a modular way. The main advantage of doing that is, the modules become reusable. I can then call the modules from the root module or call the module inside the module. We can also publish the modules to the terraform registry or your private registries in your organization. Then any team can pull the modules and terraform automatically downloads the modules using the source and version information.

In this blog, we will see how to create modules and what module_scope is, using terraform.

Module Blocks

Terraform HCL language provides us the Module Block resource, which we can use to define the multiple resources together to form one module. In each repository, we will have a minimum of one root module which is the current working directory where you initialize the terraform using terraform init. We can call the child module using the below syntax.

module "module_name" {
    source = ""
    version = ""
}
  • module_name is the local name, which can be used for referring to the instance.
  • source is the mandatory argument where we can point to a local directory containing terraform configuration files or a remote registry in which case, you specify the URL where terraform configuration files are defined.
  • version is mandatory only when you use the modules from the registry.

The module also supports input variables and meta-arguments. We can create multiple instances using one module block definition using the meta-argument count and we can iterate over the multiple instances using meta-argument for_each. We can also explicitly mention the dependencies, if there are any dependent modules that must be completed, before the current module. It is done through meta-argument depends_on

module scopes

The resources defined in one module is not visible to another module. This helps in making each resource unique in a particular module namespace. If you want to use the resources between the modules, we need to explicitly output the resource.

To understand terraform module, we need to understand how module scope works. So let us understand the module scope with an example.

Suppose we are provisioning an AWS lambda function resource and we need to associate the role to the lambda function, we can use the module concept here.

Project Structure

  • Create a new directory and move to the directory.
mkdir terraform-modules && cd terraform-modules

main.tf

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "3.50.0"
    }
  }
}

provider "aws" {
  # Configuration options
  region                  = var.region
  profile                 = var.aws_profile
  shared_credentials_file = var.shared_credentials_file
  default_tags {
    tags = var.tags
  }
}

variables.tf

variable "region" {
  description = "Deployment Region"
  default     = "ap-south-1"
}

variable "aws_profile" {
  description = "Given name in the credential file"
  type        = string
  default     = "rahul-admin"
}

variable "shared_credentials_file" {
  description = "Profile file with credentials to the AWS account"
  type        = string
  default     = "~/.aws/credentials"
}

variable "tags" {
  description = "A map of tags to add to all resources."
  type        = map(string)
  default = {
    application = "Learning-Tutor"
    env         = "Test"
  }
}
  • Create a folder modules/lambda for lambda configurations and modules/roles for role configuration.
resource "aws_lambda_function" "hello_lambda" {
  function_name = "hello-lambda"
  role = var.aws_lambda_function_role_arn
}
  • Now create a variables.tf in modules/lambda and add the below variables.
variable "aws_lambda_function_role_arn" {
  type = string
}

Now, we can call the lambda module from our root module i.e main.tf as shown below.

module "lambdas" {
  source = "./modules/lambda"
  aws_lambda_function_role_arn = module.roles.lambda_role_arn
}

As we see, for the role, we need to call another module i.e roles module, and access the resource lambda_role_arn which is defined in the roles module. As we cannot directly access the resources between each module, we have to explicitly expose it as an output as will be done next.

  • Create an iam.tf file in module/role folder and add the below content.
resource "aws_iam_role" "lambda_role" {
  name = "Hello-lambda-role"
  assume_role_policy = data.template_file.lambda_assume_role_policy.rendered
}
  • Create a source.tf file in the module/role folder and add the below content.
data "template_file" "lambda_assume_role_policy" {
  template = file("${path.module}/templates/lambda_assume_role_policy.json")
}
  • Create a folder module/role/templates and add the below policy.
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Lambda",
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Now, as we cannot access the resources from one module into another module, we need to explicitly create an output and expose the resource for another module.

  • Create a outputs.tf file in the module/role folder and add the below output.
output "lambda_role_arn" {
  value = aws_iam_role.lambda_role.arn
}

Now, we can access lambda_role_arn in another module, so we can use aws_lambda_function_role_arn = module.roles.lambda_role_arn in our lambda module as shown below.

module "lambdas" {
  source = "./modules/lambda"
  aws_lambda_function_role_arn = module.roles.lambda_role_arn
}

Conclusion

In this blog post, we saw what are Terraform modules and what a Terraform scope is. We have learned how to use the resources from one module in another module.

GitHub Repo: https://github.com/rahulmlokurte/aws-usage/tree/main/terraform/terraform-modules

Did you find this article valuable?

Support Rahul Lokurte by becoming a sponsor. Any amount is appreciated!