Infrastructure as Code (IaC) teams frequently encounter challenges when sharing Terraform modules across multiple projects and repositories. Organizations often resort to copying and pasting module code between repositories, creating maintenance overhead and version drift issues. This article examines strategies for establishing a centralized module management system similar to package management approaches used in application development.

The Module Sharing Challenge

In distributed repository architectures, teams commonly define reusable infrastructure components such as Route53 configurations, Web Application Firewalls (WAF), and Virtual Private Clouds (VPC) as Terraform modules. Without a formal sharing mechanism, these modules are often duplicated across repositories through manual copying, leading to several issues:

  • Version drift: Different projects may use outdated or inconsistent versions of the same module
  • Maintenance overhead: Bug fixes and improvements must be manually propagated across repositories
  • Lack of discoverability: Teams may unknowingly recreate existing functionality

Solution Approaches

Private Terraform Registry

Terraform supports private module registries that function similarly to package managers in application development. This approach allows modules to be referenced with semantic versioning:

module "route53" {
  source  = "your-company.com/terraform/route53"
  version = "~> 2.1.0"
  
  domain_name = "example.com"
}

Private registries can be implemented through:

  • Terraform Cloud or Terraform Enterprise
  • Self-hosted solutions
  • Git-based package registries provided by platforms like GitLab and GitHub

Git-Based Module Sourcing

Terraform natively supports referencing modules directly from Git repositories with version pinning through Git references:

module "route53" {
  source = "git::https://git.company.com/org/terraform-modules.git//route53?ref=v2.1.0"
  
  domain_name = "example.com"
}

This approach provides immediate value without requiring additional infrastructure setup. Teams can use Git tags for version management or reference specific branches for different deployment strategies.

Third-Party Dependency Management

Tools like Terragrunt extend Terraform’s dependency management capabilities, providing features such as dependency resolution and advanced configuration management.

Implementation Strategy: Phased Approach

A pragmatic implementation strategy involves starting with Git-based module sourcing and migrating to a private registry system. This approach provides immediate relief from copy-paste workflows while establishing a foundation for more sophisticated dependency management.

Phase 1: Git-Based Implementation

For organizations using trunk-based development, modules can reference the main branch directly:

module "route53" {
  source = "git::https://git.company.com/org/terraform-modules.git//route53?ref=main"
  
  domain_name = "example.com"
}

This configuration automatically retrieves the latest module version while maintaining the ability to pin to specific commits for production environments.

Phase 2: Registry Migration

GitLab provides built-in Terraform module registry functionality that integrates with existing Git workflows. Modules can be automatically published through CI/CD pipelines:

publish:
  stage: publish
  script:
    - |
      for module_dir in modules/*/; do
        module_name=$(basename "$module_dir")
        tar -czf ${module_name}-${CI_COMMIT_TAG}.tar.gz -C modules ${module_name}/
        
        curl --header "JOB-TOKEN: ${CI_JOB_TOKEN}" \
             --upload-file ${module_name}-${CI_COMMIT_TAG}.tar.gz \
             "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/terraform/modules/${module_name}/${CI_COMMIT_TAG}/file"
      done
  only:
    - tags

Centralized Module Repository Architecture

The optimal organizational structure involves maintaining a dedicated repository serving as a centralized module library:

terraform-modules/
├── modules/
│   ├── route53/
│   ├── waf/
│   ├── vpc/
│   └── rds/
├── examples/
├── .gitlab-ci.yml
└── README.md

This architecture provides several advantages:

  • Single source of truth for all shared infrastructure components
  • Centralized governance and code review processes
  • Consistent versioning across the entire module ecosystem
  • Enhanced discoverability through a unified module catalog

Module Consumption Patterns

Teams can consume modules using consistent patterns across different projects:

# Using registry approach
module "route53" {
  source  = "git.company.com/org/terraform-modules/route53"
  version = "~> 1.2.0"
  
  domain_name = var.domain_name
}

# Using Git approach
module "waf" {
  source = "git::https://git.company.com/org/terraform-modules.git//modules/waf?ref=v1.3.0"
  
  web_acl_name = var.application_name
}

Authentication and Access Control

Module registries typically integrate with existing authentication systems. GitLab’s implementation leverages project access controls and supports both personal access tokens and CI/CD job tokens for automated environments.

The phased implementation approach allows organizations to immediately address module sharing challenges while building toward a more sophisticated dependency management system. This strategy minimizes disruption to existing workflows while establishing patterns that scale across large development organizations.