Terraform module sourcing from Git repositories is a core workflow for reusable infrastructure. When a module source is a version control repository, Terraform clones the repository and resolves the module relative to the package root. The source argument controls how Terraform locates, authenticates to, and pins the module version. Understanding the full set of valid source syntaxes for GitHub, GitLab, Bitbucket and private repos, together with subdirectory selection and ref pinning, is essential for reliable, repeatable deployments.
Understanding Terraform Module Sources from Git
Terraform supports loading modules from Git repositories using either HTTPS, SSH, or GitHub short syntax. The source argument is evaluated during initialization and Get operations, not during plan. Because of this, any input variable referenced in a module block's source or version arguments must declare const = true.
If you are using modules hosted in GitHub, BitBucket, or another Git repository, 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.
When the source of a module is a version control repository or archive file, the module itself may be in a sub-directory relative to the root of the package. 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.
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 the version control sources, the sub-directory portion must be before those arguments.
GitHub HTTPS Source Patterns
Use HTTPS when you do not want SSH keys or are using public repos.
Basic HTTPS pattern:
hcl
module "vpc" {
source = "git::https://github.com/terraform-aws-modules/terraform-aws-vpc.git"
}
With subdirectory:
hcl
module "s3" {
source = "git::https://github.com/your-org/terraform-modules.git//modules/s3_bucket"
}
With tag or branch:
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"
In the following example, Terraform selects the module version from a Git repository tagged as v1.2.0:
hcl
module "vpc" {
source = "git::https://example.com/vpc.git?ref=v1.2.0"
}
You can also source a module using its SHA-1 hash:
hcl
module "storage" {
source = "git::https://example.com/storage.git?ref=51d462976d84fdea54b47d80dcabbf680badcdb8"
}
GitHub SSH Source Patterns
Use SSH if you are pulling private modules using 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].
Make sure your runner, Terraform Cloud or CI, has access via SSH key. For HTTPS auth in private repos, you can also use .netrc or GIT_ASKPASS.
GitHub Short Syntax Limitations
The short syntax is supported only for public modules.
hcl
module "vpc" {
source = "github.com/terraform-aws-modules/terraform-aws-vpc"
}
This works only for the root of the repo, with no subdirectories and no tags.
GitLab and Bitbucket Source Patterns
GitLab HTTPS:
hcl
module "firewall" {
source = "git::https://gitlab.com/your-org/infra-modules.git//firewall?ref=main"
}
Bitbucket HTTPS:
hcl
module "app" {
source = "git::https://bitbucket.org/your-team/terraform-modules.git//modules/app?ref=release-1.0"
}
Private Git repos with SSH:
hcl
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. For HTTPS auth, .netrc or GIT_ASKPASS can be used.
Anatomy of a Terraform Git Source URL
The structure of a Terraform Git source URL is:
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 |
Examples:
hashicorp/consul/aws//modules/consul-cluster
git::https://example.com/network.git//modules/vpc
https://example.com/network-module.zip//modules/vpc
s3::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, the sub-directory portion must be before those arguments:
git::https://example.com/network.git//modules/vpc?ref=v1.2.0
github.com/hashicorp/example//modules/vpc?ref=v1.2.0
Pinning Refs and Installation Behavior
Depending on the module source, you can also configure how Terraform installs the module. Refer to the source argument reference for details.
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's values will not be available. Any input variable referenced in a module block's source or version arguments must declare const = true.
Pro Tips:
| Tip | Reason |
|---|---|
| Always pin a tag using ?ref= | Prevents unexpected changes from default branch moves |
Terraform GitHub Repository Module Overview
A Terraform module for managing GitHub repositories as code exists to build on top of the Terraform GitHub Provider.
This module supports Terraform v1.x and is compatible with the Official Terraform GitHub Provider v4.20 and above from integrations/github.
Attention: This module is incompatible with the Hashicorp GitHub Provider! The latest version of this module supporting hashicorp/github provider is ~> 0.10.0
Note: Versions 5.3.0, 5.4.0, and 5.5.0 of the Terraform Github Provider have broken branch protections support and should not be used.
Features include:
- GitHub as Code
- Module Features
- Getting Started
- Module Argument Reference
- Main Resource Configuration
- Extended Resource Configuration
- Repository Creation Configuration
- Teams Configuration
- Collaborator Configuration
- Branches Configuration
- Deploy Keys Configuration
- Branch Protections v3 Configuration
- Branch Protections v4 Configuration
- Issue Labels Configuration
- Projects Configuration
- Webhooks Configuration
- Secrets Configuration
- Autolink References Configuration
- App Installations
- Module Configuration
- Module Outputs
- External Documentation
- Module Versioning
GitHub as Code is a commercial solution built on top of open-source Terraform modules for GitHub. It helps customers manage their GitHub organization more efficiently by enabling self-service management of users, repositories, and settings such as branch protections, secrets, and more through code.
Default Security Settings: This module creates a private repository by default, Deploy keys are read-only by default.
Standard Repository Features: Setting basic Metadata, Merge Strategy, Auto Init, License Template, Gitignore Template, Template Repository
Extended Repository Features: Branches, Branch Protection, Issue Labels, Handle Github Default Issue Labels, Collaborators, Teams, Deploy Keys, Projects, Repository Webhooks
Features not yet implemented: Project Columns support, Actions, Repository File
Most basic usage creating a new private github repository:
hcl
module "repository" {
source = "mineiros-io/repository/github"
version = "~> 0.18.0"
name = "terraform-github-repository"
license_template = "apache-2.0"
gitignore_template = "Terraform"
}
name: Required string. The name of the repository.
defaults: Optional object. DEPRECATED: This variable will be removed in future releases. It was needed in times when Terraform Module for each was not available to provide default values for multiple repositories.
The module follows Semantic Versioning. Given a version number MAJOR.MINOR.PATCH, we increment the MAJOR version when we make incompatible changes, MINOR version when we add functionality in a backwards compatible manner, and PATCH version when we make backwards compatible bug fixes.
Backwards compatibility in versions 0.0.z is not guaranteed when z is increased. Initial development. Backwards compatibility in versions 0.y.z is not guaranteed when y is increased.
Conclusion
Terraform module sourcing from GitHub and other Git hosts is governed by a predictable URL grammar and by the interaction of protocol, host, repository path, subdirectory selection, and ref pinning. HTTPS is convenient for public access, SSH is required for private access with deploy keys, and the GitHub short syntax trades flexibility for brevity. Subdirectory selection via double-slash is stable across Git, HTTP archive, and S3 sources, and ref pinning with tags, branches, or full SHA-1 hashes is the recommended practice to avoid drift. When modules are installed, Terraform extracts the entire package but reads only the specified subdirectory, and variables used in source or version arguments must be const. For GitHub repository lifecycle management, the Mineiros module provides a comprehensive, ready-to-use blueprint maintained for Terraform v1.x with the integrations/github provider v4.20+, with explicit incompatibility with the Hashicorp provider and known broken provider versions. Combining correct source syntax with explicit ref pinning and proper authentication yields reproducible module consumption across public and private GitHub, GitLab, and Bitbucket repositories.