Terraform Modules & Private repositories

A common way of sharing terraform code is by using modules. However, modules often end up extracted into a separate repo. This starts to create issues when integrating with a CICD tool as the tool needs to have access to all modules in order to download them.

For example, let’s assume we have the following module imported into our terraform configuration:

module "abc" {
  source = "git@github.com:username/repository"

This implies that our CICD need to have access to the username/repository.

The best way I’ve found to enforce that is to create a bot user that has read-only access to the repository. This layer is optional, but I’ve found that having a read-only user gives more granular control over repositories and increases security. Should its credentials or token be leaked, the damage is limited to only reading repositories and not pushing code to production or any other unwanted behavior.

Given a user with access to the repository, we would then create a token that will allow for reading the private module.

Finally, as part of a step in the CICD, we make sure we use the right protocol to download the module before we initialized terraform.

- name: "Setup Credentials"
    GH_TOKEN: ${{ secrets.GH_TOKEN }} # Token created for the user with access to the repository
  run: |
    git config --global url."https://anything:${GH_TOKEN}@github.com/".insteadOf "https://github.com/"
    git grep -l git\@github.com\: -- '*.tf' | xargs sed -i "s/git\@github.com\:/git::https:\/\/github.com\//g" || echo "Already modified files"    

The above snippet does two things. First it makes sure that any request that go to github.com now happen to be authenticated with the token created beforehand.

It also makes sure that any module that were downloaded via the git@github.com protocol (using ssh) would be transformed to use https instead with the user’s authentication.

Note that the last step actually modifies the files in place before performing any terraform module. Therefore it is important to make sure it runs before any terraform code.


Since this modifies code in place, we need to make sure only files we want match the regular expression. Normally, this should be the case. It also creates a diff that could be committed.


Some might advocate in favor of actually committing the code that was modified by the CICD. This would make for one less step in the CICD workflow. Personally, I prefer not to change the code to match the CICD, and have the CICD work independently. This also enables permission for all “human users” to have access be granted by their SSH keys and repositories they are given access to.