Mastering the Terraform Registry: Public and Private Modules
In modern Infrastructure as Code (IaC) workflows, efficiency is driven by reusability. Instead of writing low-level Terraform configurations from scratch for every project, DevOps teams leverage pre-built, tested, and versioned packages of code called modules. The central hub for sharing and consuming these modules is the Terraform Registry.
Whether you want to accelerate development using community-verified blueprints or secure your proprietary infrastructure designs within an enterprise network, understanding how to navigate both the Public and Private Terraform Registries is essential. This guide covers everything from basic consumption to advanced private registry architecture.
What is the Terraform Registry?
The Terraform Registry is a repository of integrations (providers) and configuration blueprints (modules) that extend Terraform's capabilities. It acts as a directory where developers can publish, discover, and consume infrastructure templates.
To understand how Terraform resolves modules from registries, consider this logical flow:
+--------------------------------------------------------+
| Terraform CLI |
+---------------------------+----------------------------+
|
| Resolves Source Path
v
+---------------+---------------+
| Is source a Registry Path? |
+---------------+---------------+
|
+-------------+-------------+
| Yes | No
v v
+---------+---------+ +---------+---------+
| Public Registry | | Local Path / Git |
| (registry. | | / S3 Bucket / HTTP|
| terraform.io) | +-------------------+
+---------+---------+
|
| Or Private Registry
v
+---------+---------+
| Private Registry |
| (app.terraform.io |
| or Self-Hosted) |
+-------------------+
The Public Terraform Registry
The Public Terraform Registry is hosted by HashiCorp and is accessible to anyone. It contains thousands of modules contributed by cloud providers (like AWS, Azure, and Google Cloud), third-party vendors, and the open-source community.
How to Use a Public Module
To use a public module, you reference its registry path in the source argument of a module block. It is a best practice to always specify a version constraint to prevent unexpected infrastructure changes when the module author publishes updates.
Here is an example of consuming the official AWS VPC module from the Public Registry:
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.1.0"
name = "production-vpc"
cidr = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b", "us-east-1c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_nat_gateway = true
enable_vpn_gateway = false
tags = {
Environment = "production"
Team = "Platform-Engineering"
}
}
When you run terraform init, Terraform parses this block, connects to the public registry, downloads the module code for version 5.1.0, and caches it locally in your .terraform directory.
The Private Terraform Registry
While public modules are excellent for standard setups, enterprises often require custom configurations that comply with internal security policies, compliance standards, and proprietary architectural patterns. Sharing these configurations via the public registry is not an option due to security risks.
This is where Private Registries come in. A private registry allows you to share modules internally across your organization while keeping the source code secure.
Options for Hosting a Private Registry
- Terraform Cloud / Terraform Enterprise: HashiCorp provides a built-in private registry out of the box. It integrates directly with your Version Control System (VCS) like GitHub, GitLab, or Bitbucket.
- Self-Hosted Solutions: Third-party artifact repositories (like JFrog Artifactory) support the Terraform Registry API protocol.
- Direct Git Sources: While not a formal "registry," you can reference private Git repositories directly using SSH or HTTPS. However, this lacks the semantic versioning and discovery features of a true registry.
How to Reference a Private Module
To reference a module in a private registry hosted on Terraform Cloud, the source address must include the registry hostname and your organization name:
module "secure_database" {
source = "app.terraform.io/my-enterprise-org/secure-db/aws"
version = "1.4.2"
db_name = "customer_prod"
allocated_storage = 100
multi_az = true
}
Authenticating with a Private Registry
Before running terraform init on a machine using private modules, you must authenticate. This is typically done by running the login command in your terminal:
terraform login
This command generates an API token and stores it in your local configuration file (~/.terraform.d/credentials.tfrc.json). For CI/CD pipelines, you can set this token using the TF_TOKEN_app_terraform_io environment variable.
Comparing Public and Private Registries
- Accessibility: Public registries are open to the global internet; private registries require authentication and are restricted to your organization.
- Security and Compliance: Private registries allow security teams to pre-approve and curate modules, ensuring developers only deploy compliant infrastructure.
- Versioning: Both support semantic versioning (e.g., 1.0.0), but private registries offer better control over deprecation and access control.
- Source Code Control: Public modules are open source; private modules remain hosted on your internal VCS repositories.
Common Mistakes and How to Avoid Them
1. Omitting Version Constraints
The Mistake: Referencing a module without a version parameter. When the module author publishes a breaking change, your next execution of terraform init -upgrade will pull the new version and potentially break your infrastructure.
The Solution: Always pin your modules to a specific version or a safe range using pessimistic operators (e.g., version = "~> 5.1.0").
2. Storing Secrets in Module Defaults
The Mistake: Hardcoding database passwords, API keys, or SSH private keys inside the variables.tf file of a published module.
The Solution: Use sensitive variables (using sensitive = true) and pass secrets at runtime using environment variables, Terraform Cloud workspace variables, or external secret managers like HashiCorp Vault.
3. Monolithic Module Design
The Mistake: Creating a single "mega-module" that deploys a VPC, database, Kubernetes cluster, and CDN all at once. This makes the module highly rigid and difficult to maintain.
The Solution: Follow the single-responsibility principle. Build small, composable modules (e.g., one for VPC, one for RDS, one for EKS) and chain them together in your root configuration.
Real-World Use Cases
Use Case 1: Multi-Account Cloud Governance
A financial institution uses a private Terraform registry to enforce network security policies. The platform engineering team publishes a secure-subnet module that automatically configures route tables, network access control lists (NACLs), and flow logs according to corporate compliance standards. Individual application teams consume this module, guaranteeing that no application is accidentally exposed to the public internet.
Use Case 2: Rapid Prototyping with Public Modules
A startup wants to quickly deploy a proof-of-concept application on AWS. Instead of spending weeks writing low-level IAM policies, security groups, and load balancer configurations, they use verified modules from the Public Terraform Registry. They launch a production-grade architecture in days, then gradually customize it as their needs evolve.
Interview Preparation Notes
What is the difference between a provider and a module in the Terraform Registry?
A provider is a plugin that translates Terraform configuration into API calls to a specific cloud or service (e.g., AWS, Azure, GitHub). A module is a packaged collection of Terraform configuration files (.tf) that groups resources together to create reusable infrastructure components.
How does Terraform authenticate to a private registry in a non-interactive CI/CD pipeline?
In automated environments, you can authenticate by setting an environment variable named TF_TOKEN_<hostname>, where <hostname> is the address of your private registry with underscores replacing periods. For example, for Terraform Cloud, you would configure TF_TOKEN_app_terraform_io containing your organization's API token.
What are "Verified Modules" in the Public Registry?
Verified modules are marked with a blue badge in the public registry. This badge indicates that the module is maintained by a trusted partner (such as AWS, HashiCorp, or MongoDB) and has undergone review to ensure high quality, security, and compatibility with the target platform.
Summary
The Terraform Registry is the backbone of reusable infrastructure code. By leveraging the Public Registry, you can stand up complex architectures in minutes using community-vetted modules. As your organization grows, transitioning to a Private Registry ensures that your infrastructure configurations remain secure, compliant, and standardized across all engineering teams.
Remember to always pin your module versions, design modules with single responsibilities, and keep sensitive credentials out of your module source files to maintain a secure and robust IaC pipeline.