Skip to content

Terraform Module Swapping

This tool provides a safe, reversible way to swap Terraform module source attributes between remote registry addresses and local relative paths for development.

Overview

The swap-terraform-modules.ps1 script automatically detects Terraform module sources pointing to app.terraform.io/SAIFCorp/... and swaps them with local relative paths, enabling local development while maintaining the ability to easily revert changes.

Files

  • swap-terraform-modules.ps1 - Main swapping tool
  • check-no-local-sources.ps1 - CI validation script
  • README-swap-terraform-modules.md - This documentation

Usage

Basic Commands

# See what would be changed (dry run)
pwsh ./scripts/swap-terraform-modules.ps1 -DryRun

# Apply changes to use local paths
pwsh ./scripts/swap-terraform-modules.ps1 -Apply

# Revert back to registry sources
pwsh ./scripts/swap-terraform-modules.ps1 -Undo

# List current operations
pwsh ./scripts/swap-terraform-modules.ps1 -ListOperations

Advanced Options

# Specify custom path
pwsh ./scripts/swap-terraform-modules.ps1 -Path src/terraform -DryRun

# Check for local sources (CI usage)
pwsh ./scripts/check-no-local-sources.ps1

How It Works

Auto-Detection

The script automatically scans .tf files for registry sources matching the pattern:

app.terraform.io/SAIFCorp/{category}/{type}//modules/{module_name}

And converts them to local paths, routing each reference to the correct local repository:

Provider Registry path Local repo Local path
azure networking/azure//modules/private_endpoint iac-azure-modules (sibling to forge) ../iac-azure-modules/modules/azure-networking/modules/private_endpoint
saif utilities/saif//modules/namer forge (current repo) src/terraform/saif-utilities/modules/namer

Prerequisite for cross-repo swaps: Azure and Okta modules are now in separate GitHub repositories (iac-azure-modules, iac-okta-modules). To swap references to those modules, clone them into the same parent directory as forge:

saif-corp/
  forge/               ← you are here
  iac-azure-modules/   ← clone from github.com/saif-corp/iac-azure-modules
  iac-okta-modules/    ← clone from github.com/saif-corp/iac-okta-modules

Example Conversion

Before (Registry Source):

module "search_diagnostic_settings" {
  source  = "app.terraform.io/SAIFCorp/observability/azure//modules/diagnostic_settings"
  version = ">= 1.0.0, < 2.0.0"
  # ... other configuration
}

After (Local Path — FullyQualified mode):

module "search_diagnostic_settings" {
  source  = "C:/tfsgit/github.com/saif-corp/iac-azure-modules/modules/azure-observability/modules/diagnostic_settings"
  version = ">= 1.0.0, < 2.0.0"
  # ... other configuration
}

Mapping Logic

The script uses this mapping logic (resolved relative to repos located as siblings of forge):

  • observability/azure//modules/diagnostic_settingsiac-azure-modules/modules/azure-observability/modules/diagnostic_settings
  • networking/azure//modules/private_endpointiac-azure-modules/modules/azure-networking/modules/private_endpoint
  • utilities/saif//modules/namersrc/terraform/saif-utilities/modules/namer (stays in forge)

Safety Features

Operation Tracking

  • Records all operations in scripts/.terraform/swap.json
  • Enables reliable undo functionality
  • Tracks file paths, original/replacement values, and context

Validation

  • Only modifies exact quoted source = "..." patterns
  • Verifies file existence before operations
  • Warns about mismatches during undo

Development Workflow

1. Start Local Development

# Check what would be changed
pwsh ./scripts/swap-terraform-modules.ps1 -DryRun

# Apply local path swapping
pwsh ./scripts/swap-terraform-modules.ps1 -Apply

# Verify changes work
terraform init
terraform plan

2. During Development

  • Edit Terraform modules locally
  • Test changes with local paths
  • All module sources point to local directories

3. Before Committing

# Revert to registry sources
pwsh ./scripts/swap-terraform-modules.ps1 -Undo

# Verify no local paths remain
pwsh ./scripts/check-no-local-sources.ps1

# Test with registry sources
terraform init
terraform plan

# Commit changes
git add -A
git commit -m "Your changes"

CI/CD Integration

Add the CI check script to your build pipeline:

# Azure DevOps example
- task: PowerShell@2
  displayName: 'Check No Local Sources'
  inputs:
    targetType: 'filePath'
    filePath: 'scripts/check-no-local-sources.ps1'
    arguments: '-Quiet'
    workingDirectory: '$(Build.SourcesDirectory)'

The CI script will fail the build if any local source paths are detected.

Troubleshooting

Common Issues

"Could not parse registry source"

  • The source format doesn't match expected pattern
  • Manually verify the source URL format

"Current source not found during undo"

  • File was manually modified after apply
  • Check the current source value manually
  • May need to manually revert specific files

"Path not found"

  • Specified search path doesn't exist
  • Use absolute paths or verify working directory

Recovery

If operations fail or files are corrupted:

  1. Use git checkout to revert file changes
  2. Delete scripts/.terraform/swap.json to reset state if needed

Best Practices

  1. Always run dry-run first to see what would be changed
  2. Run terraform init/plan after applying swaps
  3. Never commit local paths to main branches
  4. Use undo before committing to restore registry sources
  5. Test changes thoroughly in both local and registry modes

File Structure

scripts/
├── .terraform/
│   └── swap.json              # Operation tracking (generated)
├── swap-terraform-modules.ps1 # Main script
├── check-no-local-sources.ps1 # CI validation
└── README-swap-terraform-modules.md

Exit Codes

  • 0 - Success
  • 1 - Error or validation failure

Security Note

Local source modifications should only be used for development. Never commit local paths to production branches, as they will break CI/CD pipelines that don't have access to local file paths.