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>.usernameand.helper), or - a
.netrcfile withmachine,loginandpassword(permissions600), 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. |