Working with Terraform modules

3 minute read

Terraform logo

In this post you’ll see how reuse your Terraform code using modules to avoid writing the same code over and over. Also you will lear how to version your modules and how to use a specific version.

How to define a module?

Just put all your .tf files into a folder, for example:

mymodule/
├── main.tf
├── outputs.tf
├── README.md
└── variables.tf

Then copy it into a modules folder.

How to use the module?

Use the source parameter to specify the path of your module as shown below:

module "example" {

  source = "./modules/mymodule"

  var1 = "Hello World"
  var2 = 1999
}

Module’s variables

As you can see this module receives two arguments, which can be defined in the mymodule’s variables.tf file.

variable "var1" {
  description = "A string var"
}

variable "var2" {
  description = "A numerical var"
default = 1989
}

Module’s outputs

Modules also have outputs that can be used by other modules and resources. You can define them in the outputs.tf file:


output "id" {
  description = "This is the mymodule's id"
}

output "name" {
  description = "This is the mymodule's name"
}

Modules Sources

The above examples use the source parameter to retrieve the module from a local folder, but you can also use other sources like a git repository, mercurial repository, HTTP urls, S3 bucket or the Terraform Registry.

For example, instead of using a folder you can use a git repository to version your module and call it this way:

module "example" {

  source = "git@bitbucket.org:mygitrepo/mymodule.git"

  var1 = "Hello World"
  var2 = 1999
}

Module repo branch and version

You can also point to a specific branch or version in a git repository using the ?ref query. For example to specify the dev branch:

module "example" {

  source = "git@bitbucket.org:mygitrepo/mymodule.git?ref=dev"

  var1 = "Hello World"
  var2 = 1999
}

To point to version 0.0.2 use it like this:

module "example" {

  source = "git@bitbucket.org:mygitrepo/mymodule.git?ref=0.0.2"

  var1 = "Hello World"
  var2 = 1999
}

What’s the problem with this approach?

If you want to upgrade the module version and you have used it several times in your project you must edit it in every place you defined it by hand.

Module version

If your are using Terraform version v0.11.0+ you can use a specific version for a module. This help you to point to a specific version, for example to a an stable version of the module. This only works if you are using a module registry like the Terraform Registry

module "example" {
  source  = "hashicorp/mymodule"
  version = "0.0.2"

  var1 = "Hello World"
  var2 = 1999
}

What’s the problem with this approach?

  • The Terraform Registry is a public registry. For private use you must use the Private Registry available ine the Enterprise version.
  • Only available for Terraform version v0.11.0+

Using a Terrafile

There’s another approach to overcome the version pitfalls which is to write a file to define the modules to use from a git repository, by branch or version. This file si called the Terrafile:

---
# VPC
tf_aws_vpc:
  source : "git@github.com:terraform-community-modules/tf_aws_vpc.git"
  version: "master"

tf_my_module:
  source: "git@bitbucket.org:mygitrepo/mymodule.git"
  version: "0.0.2"                   

These modules will be downloaded into a modules folder and then you can reference your module using this local folder:

module "example" {

  source = "./modules/mymodule"

  var1 = "Hello World"
  var2 = 1999
}

To get the modules you can use the this Rakefile:

require 'yaml'
require 'fileutils'

# You may want to change this.
def modules_path
  'vendor/modules'
end

# You may want to change this.
def terrafile_path
  'Terrafile'
end

def read_terrafile
  if File.exist? terrafile_path
    YAML.load_file terrafile_path
  else
    fail('[*] Terrafile does not exist')
  end
end

def create_modules_directory
  unless Dir.exist? modules_path
    puts "[*] Creating Terraform modules directory at '#{modules_path}'"
    FileUtils.makedirs modules_path
  end
end

def delete_cached_terraform_modules
  puts "[*] Deleting cached Terraform modules at '#{modules_path}'"
  FileUtils.rm_rf modules_path
end

desc 'Fetch the Terraform modules listed in the Terrafile'
task :get_modules do
  terrafile = read_terrafile

  create_modules_directory
  delete_cached_terraform_modules

  terrafile.each do |module_name, repository_details|
    source  = repository_details['source']
    version = repository_details['version']
    puts "[*] Checking out #{version} of #{source} ...".colorize(:green)

    Dir.mkdir(modules_path) unless Dir.exist?(modules_path)
    Dir.chdir(modules_path) do
      `git clone -b #{version} #{source} #{module_name} &> /dev/null`
    end
  end
end

And download them using the get_modules function:

rake get_modules

Finally get the modules in Terraform

terraform get

References

Leave a Comment