If you are using HCP Terraform you have likely added one or more VCS providers1 to your organization. Doing this allows you to set up workspaces that are connected to repositories and you can have run trigger automatically when you push code to these repositories.
This blog post discuss Terraform policy or tfpolicy. It is not an introduction to tfpolicy, so you won’t learn how it works through this blog post. Read the documentation to learn more about it.
I believe there is one big issue with how VCS providers on HCP Terraform work, and it all starts with the fact that you can only have a single organization for a given SSO setup. This means if I work for a company who has set up SSO for example.com users, then we can only use these identities to access a single organization. I am not sure if this is different in the EU-instance of HCP Terraform or not, but this is the reality in the original US-based instance.
When I add a new VCS provider to my organization I can either allow everyone in my organization to use it, or I can limit its usage to specific projects. Any sane person would limit the VCS provider to specific projects, but why is that important? The answer is that because anyone who can use a given VCS provider can set up a workspace (assuming they have permissions to create workspaces, of course) and connect it to any repository available via the VCS provider.
What can you do to get around this issue? A few ideas:
- Add multiple scoped VCS providers. Each project could get their own VCS provider that is scoped to specific repositories that the project should be able to use.
- Give each project its own GitHub organization (or similar for other VCS systems) and then add a VCS provider for this organization and scope it to the correct project (this is a variant of the option above).
- Use policies.
Option 1 above fails for GitHub (and possibly other providers) because it is not possible to automate it completely. You might be able to get around it if you have a dummy user (impersonating a real person) in your GitHub organization that you could use to set up the connection, but this is still not ideal. And you can only connect a single GitHub App to your organization, then the alternative is grayed out for the rest of eternity.
Option 2 is not reasonable because you might end up with many GitHub organizations which goes directly against what GitHub recommends. It gives your platform team too much overhead, and limits an inner-source approach for your organization.
This leaves us with option 3: policies.
There are a million ways of how you can implement these policies. HCP Terraform has long supported the HashiCorp Sentinel and Open-Policy Agent (OPA) policy frameworks. Since recently HCP Terraform also supports the new Terraform policy (or tfpolicy for short) from IBM. One immediate benefit of tfpolicy is that you write policies using HCL, the language you know and love as a Terraform practitioner!
So how could we implement a solution to the problem outlined above using tfpolicy?
One idea is to rely on permissions assigned to GitHub repositories. People who are administrators for the repositories should be in control of which HCP Terraform workspaces are allowed to use it.
In a project I am currently working each “app” (broadly speaking) has an identification number. Think of it as a unique string identifying it. This identifier is used in many places, and I am experimenting using it to allow workspaces to use specific repositories on GitHub. To achieve this you simply set the unique identifier as a topic on your GitHub repository. The assumption here is that if you can set topics on a given repository, then you are also the right person to determine if a given project on HCP Terraform should be able to use the repository.
You can set the topic manually or in code (if you use Terraform to provision GitHub repositories):
# main.tf
resource "github_repository" "dummy" {
name = "my-repository"
topics = [
"my-identifier",
"...",
]
}One way of writing a policy to check that the repository is allowed for a given workspace looks like this:
# mypolicy.policy.hcl
input "unique_identifier" {
type = string
}
resource_policy "tfe_workspace" "allowed_repository" {
enforcement_level = "mandatory"
# only apply policy if a vcs repo is configured
filter = attrs.vcs_repo != null
# read the github repository in a scoped local value
locals {
repository = core::getdatasource("github_repository", {
full_name = attrs.vcs_repo[0].identifier
})
}
# check that the list of topics include the unique identifier
enforce {
condition = core::contains(local.repository.topics, input.unique_identifier)
}
}I’ve not fully tested this policy for all possible scenarios. Use it as inspiration, not absolute truth.
This policy use an input value for the unique identifier, allowing you to apply the same policy for every project using different values for the identifier.
Of course you can write the same policy using HashiCorp Sentinel or OPA. However, you will find that tfpolicy requires much less mental exercise during the authoring and debugging phases of your policy work.
Reach out on LinkedIn if you have other suggestions for how to solve this problem, or if you have better policies in place that work for your organization!
For everyone who does not understand what I am talking about: VCS means Version-Control System. Popular examples are GitHub, Gitlab and Bitbucket. ↩︎




