Enforce private module registry usage in Terraform with Sentinel
Learn how to use Sentinel policy as code in HCP Terraform to ensure developers are only using approved infrastructure modules.
In the world of infrastructure as code (IaC), governance and standardization are critical components of a successful enterprise IT strategy. Sentinel, a policy as code framework within HCP Terraform and Terraform Enterprise, provides organizations with a powerful way to enforce your standards across infrastructure deployments. Sentinel acts as a guardrail, evaluating Terraform plans against predefined policies before changes are applied to your infrastructure, ensuring compliance with organizational requirements, security best practices, and architectural standards.
One key governance concern for many organizations is controlling where Terraform modules are sourced from. Maintaining a private module registry provides a centralized, controlled repository of approved, tested, and standardized infrastructure components that meet your organization's security and compliance requirements.
However, without proper enforcement, developers might use modules from unvetted public sources, introducing potential security risks, performance issues, or non-compliant configurations. By controlling the module supply chain through automated, policy as code enforcement, organizations can ensure only modules from their private registry are used.
» Benefits of enforcing private module registry usage with Sentinel policy as code
By leveraging Sentinel policies to enforce the use of modules from your private module registry, organizations can:
- Enhance security posture by ensuring all infrastructure components come from trusted, internal sources
- Maintain compliance by using pre-approved modules that adhere to regulatory requirements
- Improve standardization and resilience across teams by encouraging reuse of consistent, well-designed modules
- Accelerate development by providing a catalog of ready-to-use, organization-specific building blocks
In this blog post, you’ll learn how to craft a Sentinel policy that enforces the use of your private module registry.

This diagram shows how the private module registry and Sentinel policy checks fit into the Terraform provisioning workflow.
» Prepare GitHub repo for Sentinel policy
The code below enforces module sourcing from approved Terraform organizations. It requires two parameters:
-
address
: The domain where your Terraform service is hosted. Defaults to "app.terraform.io". Leave unchanged when using HCP Terraform. For Terraform Enterprise, either update this default value to your deployment's domain or set this parameter explicitly when creating the Sentinel policy. -
organizations
: This parameter defines a list of allowed organization names for module sourcing
The policy evaluates module sources against this approved list. The policy fails if modules are sourced from unauthorized locations, ensuring all infrastructure is deployed using sanctioned modules from the private registry.
Note that the current policy permits resources and data sources that are declared at the root level. Additional policy constraints can be introduced to further restrict these root-level declarations if your organization wishes to implement a strictly module-based deployment approach.
import "tfconfig/v2" as tfconfig
import "tfrun"
import "strings"
param address default "app.terraform.io"
param organizations
violatingModuleCalls = filter tfconfig.module_calls as index, moduleCall {
moduleCall.module_address is "" and
not any organizations as organization {
strings.has_prefix(moduleCall.source, address + "/" + organization)
}
}
if length(violatingModuleCalls) > 0 and not tfrun.is_destroy {
print("All modules called from the root module must come from a",
"private module registry in one of these organizations:", organizations, " on server", address)
for violatingModuleCalls as address, moduleCall {
print("The module", moduleCall.name, "called from the root module has source",
moduleCall.source)
}
}
validated = tfrun.is_destroy or length(violatingModuleCalls) is 0
main = rule {
validated is true
}
This file is copied into this GitHub repository. Start by forking this repository.

Once the repository has been forked, view the sentinel.hcl
file, which defines the enforcement level for the policy. In this example, it is defined as soft-mandatory but you may also define it as advisory or hard-mandatory. Read about the differences between Sentinel enforcement levels here.

» Create Sentinel policy
This section follows similar steps to the tutorial for uploading Sentinel policy sets to HCP Terraform.
In the HCP Terraform UI, navigate to Settings > Policy Sets. Then, click on Connect a new policy set to create a new policy set.

Select your version control provider (VCS). This then leads to the Configure Settings page. Enter a name for the policy, for example enforce-pmr,
and set the scope of the policies. For demo purposes, this is set to global. Leave the rest as their default values and choose Next.

On the Connect a policy set page, choose the VCS integration configured for your organization and then choose the repository that was forked earlier. Read the Connect to VCS Providers docs for more information about VCS integrations.

Leave the rest of the settings as their defaults and on the Parameters page, choose Add parameter.

Enter the values below for the parameter. An example is shown in the screenshot below.
- Key: organizations
- Value: [“example-org”]
- Replace “example-org” with your Organization name. Add extra organizations to this list of strings if there are modules from other Organizations that are allowed.
- Note that the syntax is strict. It must be a list of strings of organization names.
Once the parameter has been set, choose Connect policy set.

At this stage you are ready to create your workspaces, private modules, and test the policy created. The rest of this blog will show how the policy can be tested.
» Testing the Sentinel policy
» Private module
Create a GitHub repository that contains a main.tf
file with the following contents. This creates a simple VPC.
variable "vpc_cidr_block" {
description = "The VPC CIDR Block"
type = string
}
resource "aws_vpc" "this" {
cidr_block = var.vpc_cidr_block
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "example-vpc"
}
}
Create a private module that references this GitHub repository. Refer to the Terraform documentation on publishing modules for detailed instructions.

» Workspaces
3 workspaces are created to test different scenarios:
- Scenario 1: Only the private module is used. The Sentinel policy passes with a run status of Needs Confirmation to confirm the
terraform apply
. - Scenario 2: Only the public module is used. The Sentinel policy fails with a run status of Policy override, which allows you to override the failure since soft-mandatory is the enforcement level setting on the Sentinel policy.
- Scenario 3: Resources and data sources are used without any modules. The Sentinel policy passes with a run status of Needs Confirmation to confirm the
terraform apply
.

» Workspace that only uses the private module
The code for this workspace uses the private module from the allowed Terraform organization. Replace <YOUR_ORGANIZATION_NAME> with your HCP Terraform organization name.
provider "aws" {
region = var.region
}
variable "region" {
description = "Region to deploy resources into"
type = string
}
variable "vpc_cidr_block" {
description = "The VPC CIDR Block"
type = string
}
module "vpc" {
source = "app.terraform.io/<YOUR_ORGANIZATION_NAME>/vpc/aws"
version = "1.0.0"
vpc_cidr_block = var.vpc_cidr_block
}
A workspace run demonstrates successful compliance with the Sentinel policy check passing. This confirms the configuration properly uses modules from the approved private registry sources.

» Workspace that only uses the public module
The code for this workspace uses a sample public AWS VPC module created for this blog from the Terraform registry.
provider "aws" {
region = var.region
}
variable "region" {
description = "Region to deploy resources into"
type = string
}
variable "vpc_cidr_block" {
description = "The VPC CIDR Block"
type = string
}
module "vpc" {
source = "GlennChia/vpc-glenn/aws"
version = "1.0.0"
vpc_cidr_block = var.vpc_cidr_block
}
The workspace run shows that the Sentinel policy failed with a message clearly indicating which modules were sourced from unauthorized locations, providing developers immediate guidance on how to meet organizational standards.
All modules called from the root module must come from a private module registry in one of these organizations: ["glenn-hcp-org"] on server app.terraform.io
The module vpc called from the root module has source GlennChia/vpc-glenn/aws

» Workspace that uses resources and data sources without modules
The code used for this workspace contains a combination of resources and data sources defined directly at the root level.
provider "aws" {
region = var.region
}
data "aws_caller_identity" "current" {}
data "aws_region" "current" {}
data "aws_partition" "current" {}
variable "region" {
description = "Region to deploy resources into"
type = string
}
variable "vpc_cidr_block" {
description = "The VPC CIDR Block"
type = string
}
resource "aws_vpc" "this" {
cidr_block = var.vpc_cidr_block
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "specific-resource-only"
}
}
A workspace run demonstrates successful compliance with the Sentinel policy check passing. This shows that the Sentinel policy does not impact root resources and data sources.

» Next steps
Enforcing private module registry usage through Sentinel policies provides organizations with effective guardrails for their infrastructure deployments. In this blog, we created a Sentinel policy linked by VCS and tested it against sample workspaces. The results demonstrate how this approach enforces private module registry usage, preventing unauthorized module sources while guiding teams toward approved infrastructure components. This governance strategy successfully balances security and compliance requirements with developer productivity. It also can significantly reduce the manual review and ticket workload for compliance teams and other IT operations approvers.
There are many other useful Sentinel policies to try out depending on your requirements. These are some references that you can use as a base for building out your Sentinel policies:
Prescriptive Sentinel policiesGitHub repository:
Pre-written Sentinel policies — co-authored by AWS and HashiCorp:
Sign up for the latest HashiCorp news
More blog posts like this one

Build secure, AI-driven workflows with Terraform and Vault MCP servers
At AWS Summit New York, HashiCorp introduced new capabilities that bring Terraform, Vault, and Vault Radar into the age of AI agents — advancing secure, automated infrastructure through composable, agentic systems.

Terraform without writing code: How to build self-service with no-code modules
Terraform no-code modules are an advanced infrastructure as code best practice that helps everyone in the org use standard, approved modules, even if you don’t know Terraform.

Helvetia’s journey building an enterprise serverless product with Terraform
What started as a basic compliance challenge for one team at Helvetia Insurance evolved into a comprehensive enterprise solution for running self-managed installations like a cloud service, using Terraform to manage a serverless architecture.