Terraform Associate
Deep Dive
Terraform supports multiple types of sources for modules: local, the public registry, GitHub, S3, and more. Knowing the exact format of each source and when to pin versions is a frequent exam topic.
Contents
| Type | Prefix / Format | Requires version? | When to use it |
|---|---|---|---|
| Local | "./modules/vpc" | ❌ No | Modules from the same repository |
| Public registry | "hashicorp/consul/aws" | ✅ Yes | Verified public modules |
| HCP registry | "app.terraform.io/org/vpc/aws" | ✅ Yes | Private corporate modules |
| GitHub HTTPS | "github.com/org/repo" | ❌ No* | Modules in public or private GitHub repos |
| GitHub SSH | "git@github.com:org/repo" | ❌ No* | GitHub repos via SSH (no token) |
| Generic Git | "git::https://..." | ❌ No* | Any Git server |
| S3 | "s3::https://bucket/module.zip" | ❌ No | Modules packaged in S3 |
| GCS | "gcs::https://bucket/module.zip" | ❌ No | Modules packaged in GCS |
* For Git sources, the ref argument is used in the URL (e.g. ?ref=v2.0.0) instead of version.
Local sources reference a directory on the same filesystem. They use relative paths (starting with ./ or ../).
# Module in a subdirectory of the same repository
module "vpc" {
source = "./modules/vpc" # relative to the calling module
}
# Module in a parent directory
module "shared" {
source = "../shared/modules/tags"
}
# version cannot be used with local sources:
# version = "~> 1.0" ← ERROR with local sources
# terraform get / terraform init — both copy the module
# into .terraform/modules/ of the working directory💡 Relative vs absolute paths
Always use relative paths (./ or ../) for local modules. Absolute paths (/home/user/modules) work on your machine but break in CI/CD or on other machines. The path is relative to the file making the call, not to the directory where you run Terraform.
# PUBLIC registry: namespace/name/provider
# (Terraform assumes registry.terraform.io as the host)
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0" # REQUIRED for the registry
}
module "iam_role" {
source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks"
version = "~> 5.30"
# The double // lets you reference a submodule within the repository
}
# PRIVATE HCP Terraform registry: app.terraform.io/org/name/provider
module "vpc" {
source = "app.terraform.io/my-company/vpc/aws"
version = "~> 2.0"
}
# Also works with your Terraform Enterprise host:
module "vpc" {
source = "terraform.my-company.com/org/vpc/aws"
version = "~> 1.0"
}🎯 Submodules with //
The double slash // in the source separates the repository from the subdirectory within it. For example: terraform-aws-modules/iam/aws//modules/iam-role references the modules/iam-role subdirectory inside the terraform-aws-modules/iam/aws module.
# GitHub HTTPS (for public repos or with an HTTPS token)
module "vpc" {
source = "github.com/my-org/terraform-vpc-module"
}
# GitHub HTTPS with ref (tag, branch or commit)
module "vpc" {
source = "github.com/my-org/terraform-vpc-module?ref=v2.0.0"
}
# GitHub SSH (authentication via SSH key)
module "vpc" {
source = "git@github.com:my-org/terraform-vpc-module.git"
}
# Generic Git (any Git server)
module "vpc" {
source = "git::https://gitlab.my-company.com/infra/vpc.git?ref=v1.5"
}
# Submodule within the repo (with //)
module "vpc" {
source = "github.com/my-org/terraform-modules//modules/vpc?ref=v3.0"
}| Git source | Authentication | ref to pin a version |
|---|---|---|
| github.com/org/repo | HTTPS token in an environment variable or ~/.netrc | ?ref=v2.0.0 or ?ref=main |
| git@github.com:org/repo | SSH key in ~/.ssh/ | ?ref=v2.0.0 |
| git::https://gitlab.com/... | Token in the URL or ~/.netrc | ?ref=tag-or-commit |
| bitbucket.org/org/repo | HTTPS token | ?ref=v1.0 |
# Amazon S3 (module packaged as .zip or .tar.gz)
module "vpc" {
source = "s3::https://s3-eu-west-1.amazonaws.com/my-bucket/vpc-module.zip"
}
# Google Cloud Storage
module "vpc" {
source = "gcs::https://www.googleapis.com/storage/v1/my-bucket/vpc-module.zip"
}
# Generic HTTP/HTTPS (any URL that returns a file)
module "vpc" {
source = "https://my-server.com/modules/vpc-1.0.0.zip"
}
# Note: these sources do not support the version argument
# The version is managed in the file name or the URL| Source type | Pin version? | How |
|---|---|---|
| Public/private registry | ✅ ALWAYS — required for the registry | version = "~> X.Y" argument |
| Local source | ❌ Not applicable | It has no versions, it is the local code |
| Git with a tag | ✅ Recommended — use a semantic tag | ?ref=v2.0.0 in the URL |
| Git with a branch | ⚠️ Not recommended in prod | ?ref=main — can change at any time |
| S3/GCS/HTTP | ✅ Recommended — in the file name | URL includes the version: module-v2.0.0.zip |
⚠️ No version on the registry = risk of breakage
If you do not specify version for a registry module, Terraform will install the latest available version on every terraform init. A new major version of the module could introduce breaking changes that break your configuration without warning.
Did you get this topic?
Test what you just learned
A team wants to use a Terraform module stored in a private company GitHub repository. They need exactly the version tagged 'v3.2.1' to always be used. What is the correct way to configure the source?
Put what you learned to the test with original Terraform Associate exam questions.
Start Terraform Associate mock examUnlimited mock exams with the Pro plan
Log in to track your progress.