Terraform Modules

Terraform has a concept called modules. These are reusable chunks of code that you can leverage. Think of them like functions or libraries. Here’s a simple example of how to call a module:

provider "aws" {
    region = "us-east-1"

module "aws-vm" {
    source          = "github.com/hooksie1/terraform-aws-module"
    name            = "test-vm"
    image           = "ami-02eac2c0129f6376b"
    size            = "t2.micro"
    key             = "test"
    cloudflare_zone = "hooks.technology"

This will build an AWS EC2 instance and apply it’s public IP to a DNS record in Cloudflare. This is all you need to call the module that will build both of those resources.

To pull in the module, just use terraform init like you would normally to pull in your provider data.

After a terraform apply we have both the EC2 instance and our record available:

Here’s the code for the module itself:


resource "aws_instance" "vm" {
    ami             = var.image
    instance_type   = var.size
    key_name        = var.key

    tags = {
        Name = var.name

data "cloudflare_zones" "default" {
    filter {
        name    = var.cloudflare_zone
        status  = "active"
        paused  = false

resource "cloudflare_record" "a_record" {
    zone_id     = data.cloudflare_zones.default.zones[0].id
    name        = var.name
    value       = aws_instance.vm.public_ip
    type        = "A"


variable name   {}
variable size   {}
variable image  {}
variable key    {}
variable cloudflare_zone {}

As you can see this module is fairly simple example, but modules can be very complex. That way all you need to do is import the module and pass in your variables.

Another flexibility with modules is you can call them multiple times. Then you could create two EC2 instances by just calling the module a second time and changing only the parameters you need to. This is especially useful when setting up multiple large infrastructures like Vault clusters in multiple regions.


Also, I hate Cloudflare’s new way to get the domain. It used to be that you could just create an A record like this:

resource "cloudflare_record" "my_record" {
   domain   = "my-domain.com"
   name    = "my-server"
   value   = aws_instance.vm.public_ip
   type    = "A"

Well now as you can see in my example, you have to get the zone data and then pass it to the resource to get the id. Not sure why they did it that way but it took a while to figure out.