Terraform modules hosted in Git repositories are a core part of reusable infrastructure as code. The source argument in a module block determines where Terraform clones the module code from and which revision to use. When the source is a Git repository, Terraform supports several address schemes that map to HTTPS, SSH, and shorthand aliases. Understanding the exact syntax for GitHub, GitLab, Bitbucket and private repositories prevents initialization failures and ensures reproducible module installs.
Introduction
Modules sourced from Git are cloned during terraform init or a terraform get operation. Terraform evaluates most variables when it creates a plan, but module installation happens before variable values are known. Any input variable referenced in a module block's source or version arguments must declare const = true. The source string can include a protocol prefix, a host and repository path, an optional subdirectory marker, and an optional ?ref= query parameter to pin a branch, tag or commit SHA-1 hash. The same mechanisms apply to public and private repositories, with different credential strategies required for private access.
Structure of a Terraform Git Source URL
A Git source URL follows a predictable pattern that Terraform parses to locate the module inside a package.
git::<protocol>://<host>/<repo>.git//<subdir>?ref=<branch|tag|commit>
| Part | Example | Role |
|---|---|---|
git:: prefix |
git:: |
tells Terraform to treat it as a Git source |
https:// or ssh:// |
https:// |
Git protocol |
//modules/x |
//modules/s3_bucket |
points to subdirectory inside the repo |
?ref= |
?ref=v1.2.0 |
tag, branch, or commit hash |
Terraform will still extract the entire package to local disk, but will read the module from the subdirectory. If the source address has arguments, such as the ref argument supported for version control sources, the sub-directory portion must be before those arguments.
Examples from the reference patterns:
git::https://example.com/network.git//modules/vpcgit::https://example.com/network.git//modules/vpc?ref=v1.2.0
GitHub HTTPS Source Patterns
HTTPS is the default choice for public repositories and for private repositories accessed with a token.
A basic HTTPS source:
module "vpc" {
source = "git::https://github.com/terraform-aws-modules/terraform-aws-vpc.git"
}
With a subdirectory:
module "s3" {
source = "git::https://github.com/your-org/terraform-modules.git//modules/s3_bucket"
}
With tag or branch pinning:
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"
Pinning a tag prevents breaking changes. Prefer HTTPS for public or Terraform Cloud because it is easier to integrate with token-based authentication.
GitHub SSH Source Patterns
SSH is used when pulling private modules using a deploy key or SSH agent.
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] format. Make sure your runner, Terraform Cloud or CI has access via SSH key. SSH avoids credential prompts in automated environments when keys are properly configured.
GitHub Short Syntax for Public Modules
Terraform will recognize unprefixed github.com URLs and interpret them automatically as Git repository sources.
module "vpc" {
source = "github.com/terraform-aws-modules/terraform-aws-vpc"
}
This works only for the root of the repo, no subdirectories, no tags. The above address scheme will clone over HTTPS. To clone over SSH, use the following form:
module "consul" {
source = "[email protected]:hashicorp/example.git"
}
These GitHub schemes are treated as convenient aliases for the general Git repository address scheme, and so they obtain credentials in the same way and support the ref argument for selecting a specific revision.
GitLab and Bitbucket HTTPS Patterns
The same git:: prefix works across hosts.
GitLab HTTPS:
module "firewall" {
source = "git::https://gitlab.com/your-org/infra-modules.git//firewall?ref=main"
}
Bitbucket HTTPS:
module "app" {
source = "git::https://bitbucket.org/your-team/terraform-modules.git//modules/app?ref=release-1.0"
}
Private Git repos with SSH:
module "db" {
source = "git::ssh://[email protected]/your-org/terraform-db-modules.git//rds?ref=main"
}
Make sure your runner has access via SSH key. HTTPS auth in private repos can also use .netrc or GIT_ASKPASS.
Private Module Access and Authentication
Access Private Terraform Modules in GitHub Actions explains how to securely pull a Terraform module from a private GitHub repository using HTTPS and a GitHub Personal Access Token. Terraform clones and uses the default branch referenced by HEAD. You can add the ref query parameter to the location specified in the source argument to reference any value supported by the git checkout command, such as a branch, SHA-1 hash, or tag.
Version pinning examples:
module "vpc" {
source = "git::https://example.com/vpc.git?ref=v1.2.0"
}
module "storage" {
source = "git::https://example.com/storage.git?ref=51d462976d84fdea54b47d80dcabbf680badcdb8"
}
You can provide a specific version as shown in the above examples, or use flexible version constraints. To access modules from a private registry, you may need to configure an access token in the CLI config. Use the same hostname as used in the module source string.
Subdirectory Selection and Ref Argument Ordering
A special double-slash syntax // is interpreted by Terraform to indicate that the remaining path after that point is a sub-directory within the package.
Examples:
hashicorp/consul/aws//modules/consul-clustergit::https://example.com/network.git//modules/vpchttps://example.com/network-module.zip//modules/vpcs3::https://s3-eu-west-1.amazonaws.com/examplecorp-terraform-modules/network.zip//modules/vpc
If the source address has arguments, such as the ref argument supported for version control sources, the sub-directory portion must be before those arguments:
git::https://example.com/network.git//modules/vpc?ref=v1.2.0github.com/hashicorp/example//modules/vpc?ref=v1.2.0
Real World Example
A repository structure like:
terraform-infra-modules/
├── vpc/
├── eks/
└── s3/
Can be referenced as:
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"
}
This isolates each module to its own directory while sharing a single monorepo.
Pro Tips
- Always pin a tag using
?ref=to prevent breaking changes. - Use double slashes
//before module subdir because it is required for subdirectory paths. - Prefer HTTPS for public or Terraform Cloud because it is easier to integrate.
- Use SSH only if you have configured keys properly to avoid access errors.
Conclusion
Mastering Terraform module sources from GitHub requires consistent use of the git:: prefix, correct protocol selection, explicit subdirectory markers, and revision pinning with ?ref=. HTTPS with tokens works well for CI and Terraform Cloud access to private repositories, while SSH provides a clean key-based alternative for private modules. Short syntax aliases for github.com simplify public module references but do not support subdirectories or tags without the full git:: form. When modules live in monorepos, the double-slash subdirectory syntax combined with a pinned ref delivers reproducible installs. Proper credential configuration, ref pinning, and awareness that module installation occurs before variable evaluation are the practical foundations for reliable Terraform module consumption from GitHub and other Git hosts.
Sources
- https://www.devopsschool.com/blog/terraform-source-for-modules-from-git-all-patterns-examples/
- https://www.grinntec.net/guides-%26-labs/github/cross-access-github-repos/
- https://developer.hashicorp.com/terraform/language/modules/configuration
- https://docs.hashicorp.com/terraform/language/modules/sources