Skip to content

Module Source Access on GitHub Enterprise

Some Terraform code that the platform runs (for example the spoke extensions for GitHub) loads its modules from Git repositories on a GitHub Enterprise host. Terraform has to authenticate to that host when it runs terraform init. This page shows the options, which one to use, and how to find out why access fails.

Why this is needed

Terraform does not allow variables in the source of a module. The URL is therefore fixed in the code, and the credentials must come from outside, from Git. A module source looks like this (placeholders):

source = "git::https://<github host>/<organization>/<module repository>.git//src?ref=<version>"

Terraform calls Git to clone it. If Git has no credentials for the host, terraform init fails with an authentication or "repository not found" error.

Choose a method

Method Use when Notes
HTTPS with a Git credential (recommended) Pipelines and agents A short-lived GitHub App installation token in a Git credential. The sections below show how.
SSH You already manage SSH keys on the agents Change the module source to the git::ssh:// form. Needs a key that is allowed to read the repositories, added to the agent as a secret.
Local module path The modules are copied into your own repository Use a relative path in source. You are then responsible for updating the copy.
Another registry Your organization has a module registry Source changes accordingly.

Use HTTPS with a token for pipelines. Tokens can be short-lived and scoped, and nothing has to be installed on the agent.

HTTPS with Git credentials

The token

The platform workflows use a GitHub App installation token. It is created in the workflow from the app ID and private key (Prepare spoke integrations). It expires on its own, so a leaked token is of limited use.

  • Store the app ID as an organization variable and the private key as an organization secret.
  • Limit access to the configuration repositories only.
  • The app must be installed in the organization that holds the modules, and be allowed to read those repositories. The permissions are listed in Prepare spoke integrations.

GitHub Actions

Create the token, then give Git the credential before terraform init. A shortened workflow:

- name: Create GitHub App token
  id: app_token
  uses: actions/create-github-app-token@v2
  with:
    app-id: ${{ vars.cloud_platform_gh_app_id }}
    private-key: ${{ secrets.cloud_platform_gh_app_private_key }}
    owner: ${{ github.repository_owner }}

- name: Configure Git credentials
  run: |
    git config --global credential.helper store
    echo "https://${{ steps.app_token.outputs.token }}@<github host>" > ~/.git-credentials

- name: Terraform init
  run: terraform init
  env:
    TF_VAR_github_token: ${{ steps.app_token.outputs.token }}

Azure DevOps

Keep the token in a variable group or a pipeline secret, and write the credential in a script step before terraform init:

steps:
- script: |
    git config --global credential.helper store
    echo "https://$(GITHUB_TOKEN)@<github host>" > ~/.git-credentials
  displayName: Configure Git credentials

- script: terraform init
  displayName: Terraform init
  env:
    TF_VAR_github_token: $(GITHUB_TOKEN)

Do not leave tokens on shared agents

The ~/.git-credentials file holds the token in plain text. On self-hosted agents that are reused, remove the file when the job ends, or use an ephemeral agent. A token that is stored on an agent for longer than the job is a leak waiting to happen.

Other ways to give Git a credential

For a person who tests on a workstation, the same result can be reached with Git configuration:

  • a credential helper that is configured for the host (credential.<url>.username and .helper), or
  • a .netrc file with machine, login and password (permissions 600), or
  • environment variables that your own credential script reads.

Do not use these on shared build agents.

SSH

Change the source to the SSH form:

source = "git::ssh://git@<github host>/<organization>/<module repository>.git//src?ref=<version>"

Create a key pair, add the public key to an account (or as a deploy key) that can read the repositories, and add the private key to the pipeline as a secret. Make the agent trust the host. In GitHub Actions an SSH agent action can load the key.

Local copies

Copy the modules into the repository and refer to them by relative path:

source = "../../../modules/<module name>/src"

Use this when the host cannot be reached at all. Add the update of the copy to your upgrade procedure (New version).

Test and troubleshoot

Test with Git first, then Terraform:

git ls-remote https://<github host>/<organization>/<module repository>.git
terraform init -backend=false
Symptom Check
Authentication failed The app is installed in the organization and has the right permissions. The token has not expired. The credential is written for the right host name.
Repository not found The repository exists on the enterprise host. The app may read it. A missing permission can show up as "not found".
Network error or timeout The agent can reach the host (firewall rules, proxy). See Network access of the agents.
init works locally but not in the pipeline The credential step runs before terraform init, in the same job and user context.