Terraform Module Example GitHub: Complete Source Patterns and Configuration

Terraform modules can be loaded from a Git repository using a source string that tells Terraform where to clone the code and which revision to use. The git source syntax is consistent across GitHub, GitLab, Bitbucket and private repositories, with variations for HTTPS, SSH and short syntax. The patterns control how Terraform clones the repository during terraform init and which subdirectory or commit is used for the module.

GitHub Source Patterns

GitHub is the most common host for Terraform modules. Terraform supports loading modules from GitHub using HTTPS, SSH or the short syntax for public modules.

GitHub HTTPS is the default choice for public repositories and for use with Terraform Cloud where SSH keys are not configured.

hcl module "vpc" { source = "git::https://github.com/terraform-aws-modules/terraform-aws-vpc.git" }

When a module lives in a subdirectory of a repository, the source uses double slashes to point to the subdir.

hcl module "s3" { source = "git::https://github.com/your-org/terraform-modules.git//modules/s3_bucket" }

A ref query parameter selects a branch, tag or commit. This pins the module version and prevents unexpected changes.

hcl source = "git::https://github.com/your-org/terraform-modules.git//modules/s3_bucket?ref=v1.2.0" source = "git::https://github.com/your-org/terraform-modules.git//modules/s3_bucket?ref=main"

GitHub SSH is used for private modules when access is provided via a deploy key or SSH agent.

hcl module "eks" { source = "git::ssh://[email protected]/your-org/terraform-modules.git//modules/eks_cluster?ref=main" }

Use git::ssh:// for SSH. Do not use raw [email protected] without the scheme.

The short syntax is available only for public modules at the root of a repo with no subdirectory and no tag.

hcl module "vpc" { source = "github.com/terraform-aws-modules/terraform-aws-vpc" }

This works only for the root of the repo. No subdirectories and no tags are supported with short syntax.

GitLab and Bitbucket Source Patterns

The same git source pattern applies to GitLab and Bitbucket.

GitLab HTTPS example:

hcl module "firewall" { source = "git::https://gitlab.com/your-org/infra-modules.git//firewall?ref=main" }

Bitbucket HTTPS example:

hcl module "app" { source = "git::https://bitbucket.org/your-team/terraform-modules.git//modules/app?ref=release-1.0" }

Private Git repositories with SSH:

hcl module "db" { source = "git::ssh://[email protected]/your-org/terraform-db-modules.git//rds?ref=main" }

Make sure your runner, Terraform Cloud or CI has access via SSH key. For HTTPS authentication in private repos you can also use .netrc or GIT_ASKPASS.

Structure of a Terraform Git Source URL

A complete Git source URL follows a predictable structure.

git::<protocol>://<host>/<repo>.git//<subdir>?ref=<branch|tag|commit>

Part Example
git:: prefix tells Terraform to treat it as a Git source
https:// or ssh:// Git protocol
//modules/x points to subdirectory inside the repo
?ref= tag, branch, or commit hash

Terraform clones and uses the default branch referenced by HEAD when no ref is specified. Adding the ref query parameter allows referencing any value supported by git checkout, such as a branch, SHA-1 hash, or tag.

Example with a tag:

hcl module "vpc" { source = "git::https://example.com/vpc.git?ref=v1.2.0" }

Example with a SHA-1 hash:

hcl module "storage" { source = "git::https://example.com/storage.git?ref=51d462976d84fdea54b47d80dcabbf680badcdb8" }

Pinning Revisions and Ref Usage

Terraform evaluates the value of most variables when it creates a Terraform plan. Since Terraform installs modules when a workspace is initialized or during a Get operation, those variable values will not be available. Any input variable referenced in a module block's source or version arguments must declare const = true.

You can select a module source using a constant variable:

hcl module "consul" { source = var.module_source } variable "module_source" { type = string const = true }

You can also compose a source from constant variables and locals that refer to constant variables:

hcl module "vpc" { source = local.vpc_source } variable "module_repo" { type = string const = true } variable "module_ref" { type = string const = true } locals { vpc_source = "git::https://example.com/${var.module_repo}.git?ref=${var.module_ref}" }

The version argument also accepts expressions. If a version expression refers to an input variable, that variable must also set const = true.

hcl module "consul" { source = "hashicorp/consul/aws" version = var.consul_version } variable "consul_version" { type = string const = true }

The source and version expressions can only reference things that are known during configuration loading. Terraform will show an error if they reference something that is only known after a plan.

Pro Tips for Git Module Sources

Tip Reason
Always pin a tag using ?ref=.. Prevents breaking changes
Use double slashes // before module subdir Required for subdirectory paths
Prefer HTTPS for public or Terraform Cloud Easier integration
Use SSH only if you’ve configured keys properly Avoids access errors

Real-World Repository Layout Example

A repository can contain multiple modules with a common folder structure.

terraform-infra-modules/ ├── vpc/ ├── eks/ └── s3/

Using this layout:

hcl module "vpc" { source = "git::https://github.com/acme-corp/terraform-infra-modules.git//vpc?ref=v1.0.0" } module "eks" { source = "git::ssh://[email protected]/acme-corp/terraform-infra-modules.git//eks?ref=main" }

Creating a Module for Git Hosting

Terraform treats any local directory referenced in the source argument of a module block as a module. A typical file structure for a new module is:

  • LICENSE
  • README.md
  • main.tf
  • variables.tf
  • outputs.tf

None of these files are required, or have any special meaning to Terraform when it uses your module. You can create a module with a single .tf file, or use any other file structure you like.

Each file serves a purpose:

  • LICENSE will contain the license under which your module will be distributed. When you share a module, the LICENSE file will let people using it know the terms under which it has been made available. Terraform itself does not use this file.
  • README.md will contain documentation describing how to use your module, in markdown format. Terraform does not use this file, but services like the Terraform Registry and GitHub will display the contents of this file to people who visit your module's Terraform Registry or GitHub page.
  • main.tf will contain the main set of configuration for your module. You can also create other configuration files and organize them however makes sense for your project.
  • variables.tf will contain the variable definitions for your module. When your module is used by others, the variables will be configured as arguments in the module block.

The tutorial workflow for a new module includes cloning a starting repository and initializing Terraform.

$ git clone https://github.com/hashicorp-education/learn-terraform-modules-create $ cd learn-terraform-modules-create $ terraform init

Initializing modules:

Downloading registry.terraform.io/terraform-aws-modules/ec2-instance/aws 4.3.0 for ec2instances...
- ec2
instances in .terraform/modules/ec2instances
Downloading registry.terraform.io/terraform-aws-modules/vpc/aws 3.18.1 for vpc...
- vpc in .terraform/modules/vpc
- website
s3_bucket in modules/aws-s3-static-website-bucket
Initializing the backend...
Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Installing hashicorp/aws v4.49.0...
- Installed hashicorp/aws v4.49.0 (signed by HashiCorp)
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure

The examples assume you are using the shared credentials file method with the default AWS credentials file and default profile. Requirements are the AWS CLI and the Terraform CLI.

Conclusion

Using Terraform modules from Git requires precise source strings and consistent pinning strategies. HTTPS sources are simplest for public modules and for Terraform Cloud integration, while SSH sources provide secure access to private repositories when keys are properly configured. The double slash syntax for subdirectories and the ?ref= query parameter for branches, tags and commit hashes are essential for reliable module consumption. Constant variables are required when composing sources dynamically because Terraform installs modules during init before plan-time values are known. Building modules with a clear file structure and documented README makes them reusable across teams and hosts. Pinning with tags, using SSH only when necessary, and separating modules into subdirectories within a monorepo are practices that reduce drift and improve supply chain security.

Sources

  1. DevOps School Blog
  2. HashiCorp Terraform Language Modules Configuration
  3. HashiCorp Terraform Tutorials Modules Create

Related Posts