Utilising Modules in Terraform

Mohak
Searce
Published in
9 min readMay 3, 2022

--

What is Terraform?

Terraform is a tool for building, changing, and versioning infrastructure safely and with efficiency. Terraform will manage existing, well-liked service suppliers and custom in-house solutions.

Key Features:

Infrastructure as code
Infrastructure is defined using a high-level configuration syntax. This ensures a blueprint of your information centre to be versioned and treated as you’d the other code. In addition, infrastructure will be shared and re-used.

Execution plans
Terraform features a coming up with step within which it generates associate execution set up. The execution set up shows what Terraform can do once you execute the apply command. This permits you to avoid any surprises once Terraform manipulates infrastructure.

Resource graph
Terraform builds a graph for all of your resources and parallelises the creation and modification of any independent resources. Owing to this, Terraform builds infrastructure as with efficiency, and operators get insight into dependencies in their infrastructure.

Change automation
Complex change sets will be applied to your infrastructure with least human interaction. With the earlier mentioned execution setup and resource graph, you now specifically know what Terraform can modify and in what order, that helps you avoid several human errors.

A simple workflow for deployment will follow closely to the steps below:

  • Scope — Confirm what resources need to be created for a given project.
  • Author — Create the configuration file in HCL based on the scoped parameters.
  • Initialise — Run terraform init in the project directory with the configuration files. This will download the correct provider plug-ins for the project.
  • Plan & Apply — Run terraform plan to verify creation process and then terraform apply to create real resources as well as the state file that compares future changes in your configuration files to what actually exists in your deployment environment.

As you manage your infrastructure with Terraform, more and more complicated configurations are created. There’s no limit to the complexity of one’s Terraform configuration file or directory, thus it’s potential to continue writing and change your configuration files during a single directory. However, if you do, you will encounter one or additional of the subsequent problems:

  • Understanding and navigating the configuration files can become more and more tough.
  • Updating the configuration can add more additional risk, because as a result of update to one block might cause unplanned consequences to alternative blocks of your configuration.
  • Duplication of same blocks of configuration might increase, for instance, when you put together separate dev/staging/production environments, which can cause increased burden while changing those components of your configuration.
  • If you would like to share components of your configuration between projects and teams, cutting and pasting blocks of configuration between comes can be erring and onerous to keep up.

Why Modules ?

Here are a number of the ways in which modules facilitate solve the issues listed above:

Organise configuration: Modules create it easier to navigate, understand, and update your configuration by keeping connected components of your configuration together. Even moderately advanced infrastructure will need thousands of lines of configuration to implement. By the use of modules, you’ll be able to organise your configuration into logical elements.

Encapsulate configuration: Another good thing about utilising modules is to encapsulate configuration into distinct logical elements. Encapsulation can stop accidental consequences — such as a amendment to one part of your configuration accidentally inflicting changes to alternative infrastructure — and cut back the possibilities of straightforward errors like using similar or same name for two completely different resources.

Re-use configuration: Writing all of your configuration while not utilising existing code may be long and fallible. Using modules will save time and cut back expensive errors by re-using configuration written either by yourself, other members of your team, or alternative Terraform practitioners who have written modules for you to use. You’ll be able to additionally share modules that you just have written along with your team or the overall public, giving them the good thing about your diligence.

Provide consistency and ensure best practices: Modules additionally facilitate to provide consistency in your configurations. Consistency makes advanced configurations easier to know, and it additionally helps to confirm that best practices applied across all of your configuration.

What is a Terraform module?

A Terraform module is a set of Terraform files in a single directory. Even a simple configuration consisting of a single folder with one or more .tf extension files is a module. When you run Terraform commands directly from such a folder, it is called as the root module.

Calling modules

Terraform commands only directly uses the configuration files in one directory, which would be the current working directory. However, your configuration can use module to call modules in other directories. When Terraform fetches a module block, it loads and processes that module’s configuration files. A module that is called by another configuration is referred to as a “child module” of the configuration.

Create a module

Navigate to your home directory and create your root module by creating a new main.tf file. Now create a directory as modules which contains another folder as gcs-static-website-bucket. You will now create three Terraform files inside the gcs-static-website-bucket directory: website.tf, variables.tf, and outputs.tf.

  1. Create the directory :
cd ~
touch main.tf
mkdir -p modules/gcs-static-website-bucket

2. Navigate to the module directory and run the following commands to create three files:

cd modules/gcs-static-website-bucket
touch website.tf
touch variables.tf
touch outputs.tf

3. Inside the gcs-static-website-bucket directory, create a file asREADME.md with the content:

# GCS static website bucket
This module constructs Cloud Storage buckets for hosting static website.

4. Create a file called LICENSE with the content:

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Your current module directory structure might look like this:

modules/
└── gcs-static-website-bucket
├── LICENSE
├── README.md
├── website.tf

5. Add this Cloud Storage resource to your website.tf config file inside the modules/gcs-static-website-bucket directory:

resource "google_storage_bucket" "bucket" {
name = var.name
project = var.project_id
location = var.location
storage_class = var.storage_class
labels = var.labels
force_destroy = var.force_destroy
uniform_bucket_level_access = true
versioning {
enabled = var.versioning
}
dynamic "retention_policy" {
for_each = var.retention_policy == null ? [] : [var.retention_policy]
content {
is_locked = var.retention_policy.is_locked
retention_period = var.retention_policy.retention_period
}
}
dynamic "encryption" {
for_each = var.encryption == null ? [] : [var.encryption]
content {
default_kms_key_name = var.encryption.default_kms_key_name
}
}
dynamic "lifecycle_rule" {
for_each = var.lifecycle_rules
content {
action {
type = lifecycle_rule.value.action.type
storage_class = lookup(lifecycle_rule.value.action, "storage_class", null)
}
condition {
age = lookup(lifecycle_rule.value.condition, "age", null)
created_before = lookup(lifecycle_rule.value.condition, "created_before", null)
with_state = lookup(lifecycle_rule.value.condition, "with_state", null)
matches_storage_class = lookup(lifecycle_rule.value.condition, "matches_storage_class", null)
num_newer_versions = lookup(lifecycle_rule.value.condition, "num_newer_versions", null)
}
}
}
}

6. Navigate to the variables.tf file in your module and add the given code:

variable "name" {
description = "Name of the bucket."
type = string
}
variable "project_id" {
description = "Your project ID."
type = string
}
variable "location" {
description = "The location of bucket."
type = string
}
variable "storage_class" {
description = "The Storage Class of the bucket."
type = string
default = null
}
variable "labels" {
description = "A set of key value pairs to assign to the bucket."
type = map(string)
default = null
}
variable "bucket_policy_only" {
description = "Enables Bucket Policy Only access to a bucket."
type = bool
default = true
}
variable "versioning" {
description = "While set to true, versioning is enabled for the bucket."
type = bool
default = true
}
variable "force_destroy" {
description = "When deleting a bucket, this boolean will delete all objects. If false, Terraform will fail to delete buckets which contain objects."
type = bool
default = true
}
variable "iam_members" {
description = "The list of IAM members to grant permissions on the bucket."
type = list(object({
role = string
member = string
}))
default = []
}
variable "retention_policy" {
description = "Configuration of the bucket's data retention policy how long objects in the bucket can be retained."
type = object({
is_locked = bool
retention_period = number
})
default = null
}
variable "encryption" {
description = "A Cloud KMS key that will be used to encrypt objects inside the bucket"
type = object({
default_kms_key_name = string
})
default = null
}
variable "lifecycle_rules" {
description = "The Lifecycle Rules configuration."
type = list(object({
# Object with keys:
# - type - The type of the action o

7. Adding an output to module in the outputs.tf file inside your module:

output "bucket" {
description = "The storage bucket"
value = google_storage_bucket.bucket
}

8. Return to the main.tf in root directory and add given code to the new module:

module "gcs-static-website-bucket" {
source = "./modules/gcs-static-website-bucket"
name = var.name
project_id = var.project_id
location = "us-east1"
lifecycle_rules = [{
action = {
type = "Delete"
}
condition = {
age = 365
with_state = "ANY"
}
}]
}

9. Create an outputs.tf file for your root module:

cd ~
touch outputs.tf

10. Add the code in the outputs.tf file:

output "bucket-name" {
description = "Bucket name."
value = "module.gcs-static-website-bucket.bucket"
}

11. Create a variables.tffile:

touch variables.tf

12. Add the below code to the variables.tf file and define the variables project_id and name:

variable "project_id" {
description = "The ID of the project."
type = string
default = "YOUR PROJECT ID HERE"
}
variable "name" {
description = "Name of the buckets to create."
type = string
default = "YOUR BUCKET NAME HERE"
}

Install the local module

When you add a new module to a configuration, Terraform should install the module before it can be used. Both terraform get & terraform init commands will install and update modules. The terraform init command will even initialise backends and install plugins.

  1. Install the module:
terraform init

2. Provision your bucket:

terraform apply

3. Respond yes to the prompt. Your bucket and other resources will be created.

Upload files to the bucket

As you now have configured and used your own module to host a static website. You might want to visit your static website. As of now there is nothing inside your bucket, so there is nothing to see at webpage. In order to see any content, you will need to upload objects to the bucket. You can upload the contents of the www directory in the GitHub repository.

  1. Download the sample contents to your home directory:
cd ~
curl https://raw.githubusercontent.com/hashicorp/learn-terraform-modules/master/modules/aws-s3-static-website-bucket/www/index.html > index.html
curl https://raw.githubusercontent.com/hashicorp/learn-terraform-modules/blob/master/modules/aws-s3-static-website-bucket/www/error.html > error.html

2. Copy the files to the bucket, replace YOUR-BUCKET-NAME with the name of your storage bucket:

gsutil cp *.html gs://YOUR-BUCKET-NAME

3. In a new tab, go to the website https://storage.cloud.google.com/YOUR-BUCKET-NAME/index.html, replacing YOUR-BUCKET-NAME with the name of your bucket.

Destroy the infrastructure

Now you will clean up your project by destroying the infrastructure you provisoned. Destroy the Terraform resources by:

terraform destroy

Respond yes & Terraform will destroy all of the services you created by following this article.

Summing up!

In this article, you discovered the creation of Terraform modules and how to use an already existing module from the Terraform Registry. You then built your own module to host a static website hosted on a Cloud Storage bucket i.e. GCS . In doing so, you configured inputs, outputs, and variables for your configuration files and explored the best-practices for building & running modules.

I hope you learn & share knowledge !

--

--