Skip to content

Access Private Terraform Modules in GitHub Actions

This guide explains how to securely pull a Terraform module from a private GitHub repository using HTTPS and a GitHub Personal Access Token (PAT). This is ideal for CI/CD workflows where module sources must remain private but accessible.


Scenario

You have two GitHub Organizations.

GitHub Owner GitHub Organization GitHub Repository Purpose
Admin deployment-org deployment-repo-x Terraform IaC used to CRUD resources
Admin module-org terraform-azurerm-<resource_x> Terraform IaC reusable modules

Your IaC in the deployment-repo references the module in the module-repo meaning when it runs in a GitHub Action the deployment-repo needs to have credentials to pull the module-repo data.

image

Step 1: Create a fine-grained, repository-scoped token suitable for personal API use and for using Git over HTTPS

This is configured at the GitHub Owner level, the layer at the top of Organizations

  1. Goto Settings → Developer Settings → Personal access tokens → Fine-grained tokens
  2. Select Generate new token
  3. Give the PAT a good name, good practice is to make it immediately clear what it’s for, where it’s used, and when it was created — so that months from now, you can glance at the list and know exactly which ones to keep or revoke.
    • For this example we'll call it azure_deploy_to_module_ro_2025_09
  4. Select the GitHub Organization for the module-org as the Resource Owner
  5. Set an expiration date that suits your security policy
  6. For best security, only grant access to the selected repositories in your module organization that are needed
  7. Limit access to the following
    • Contents : Read-Only
    • Metadata
  8. Generate the token

Warning

The token is visible once at creation then never again

Step 2: Add the PAT to the deployment repo

  1. Go to Settings → Secrets and variables → Actions in your deployment repo in the deployment org.
  2. Create a new repository secret, use the same name as the PAT created earlier or use a generic name. If you create a generic one it's less admin when you rotate the secret value as you only need to change it here and all the workflow files don't need to be changed. For example, use azure_deploy_to_module_ro.
  3. Paste the token value.

Step 3: Use the PAT in your GitHub Action file

To use a Personal Access Token (PAT) in GitHub Actions, you first declare it as a required secret in your workflow file. In the example below, the secret is called AZURE_DEPLOY_TO_MODULE_RO. The workflow makes this secret available as an environment variable for the job and configures Git to use it for any https://github.com/ URL, enabling access to private repositories or modules.

This setup ensures that Git operations in your workflow (such as cloning private Terraform modules) will authenticate using your PAT.

name: Terraform Plan
description: Run terraform plan via script

on:
  workflow_call:
    secrets:
      AZURE_DEPLOY_TO_MODULE_RO:
        required: true

jobs:
  terraform-plan:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Configure Git to use secret for module access
        run: |
          git config --global url."https://${{ secrets.AZURE_DEPLOY_TO_MODULE_RO }}@github.com/".insteadOf "https://github.com/"

      - name: Run Terraform Plan
        env:
          AZURE_DEPLOY_TO_MODULE_RO: ${{ secrets.AZURE_DEPLOY_TO_MODULE_RO }}
        run: |
          terraform plan

Step 4: Use HTTPS Source in Terraform

In the deployments themselves you just need to reference the module using HTTPS as below.

module "azure_resource_group" {
  source = "git::https://github.com/acme-terraform-modules-azure/terraform-azurerm-resource_group.git?ref=main"
}

No need to embed credentials in the URL—GitHub Actions handles authentication via the config.