Private Registry
The Terraform private registry allows organizations to host and share their own Terraform modules and providers privately, limiting access to their own network or cloud environment. It can also serve as a gateway to whitelisted Terraform modules and providers sourced from the public registry. The Terraform private registry provides an alternative to the public Terraform Registry and enables organizations to manage and maintain their infrastructure as code more effectively. The two main methods for accessing the private registry are the web UI and the API
Feature | Public Registry | Private Registry |
---|---|---|
Access/Visibility | Anyone | Only authorized users in your organization |
Content | All public modules and providers, TFC Run Tasks and Sentinel Policies. | Whitelisted public modules and providers, and your custom modules and providers. |
The private registry has three main functions: sharing, discovery, and consumption of modules and providers. We’ll cover those functions and more in this section.
Roles and responsibilities
When configuring and using the private registry, three roles must be considered:
- Registry Administrator
- Producer
- Consumer
Registry Administrator
The Registry Administrator role is tasked with these responsibilities:
- Publish and delete modules in the organization's private registry, either from the public registry or from private sources.
- Publish and delete providers in the organization's private registry, either from the public registry or from private sources.
It is an organization-level permission in TFC/E and can be assigned to a specific team:
Examples of personas needing the Registry Administrator role in an organization:
- Platform team members.
- CI/CD pipelines automating the test and publishing process for custom modules or providers.
Producer
The Producer role is tasked with:
- Create and maintain the module or provider
- Publish a new release of the module or provider
The Producer needs “commit” access to the module or provider VCS repository, and the permissions of a Registry Administrator.
Examples of personas needing the Producer role in an organization:
- The Platform team members responsible for maintaining custom modules for your organization.
- The Service Owner team members responsible for maintaining custom modules for your organization.
HashiCorp recommends that the teams with the Producer role delegate the module release publishing activity to a CI/CD pipeline with the appropriate “Registry Administration” permission (either module or provider publishing). Those teams would not have “Registry Administration” permissions, but the pipeline configuration would be configured with a Team API token with the “Registry Administration” permission. That API token has to be properly rotated or managed with a tool such as HashiCorp Vault and the Terraform Cloud secrets engine.
Consumer
The Consumer role is tasked with:
- Find providers and modules necessary to provision infrastructure.
- Use providers and modules in Terraform configuration.
To perform their role, the Consumer requires "commit" access to the VCS repository and write access to the HCP Terraform workspaces that use the Terraform configuration.
Example of personas needing the Consumer role in an organization:
- Members of application teams that are managing infrastructure using TFC/E using modules and providers.
- Members of the platform team using the TFE provider to manage the TFC/E configuration.
Sharing custom Terraform modules
Before modules and providers can be discovered and consumed from the private registry, they must first be shared.
Custom Terraform modules allow your organization to take advantage of the reusability aspect of Infrastructure as Code (IaC). They allow you to encapsulate common patterns into a parametrized Terraform unit of code and reuse those patterns across your IaC code base.
Early in your Terraform journey, your teams will discover infrastructure patterns that are optimal for your organization. Creating modules that implement those optimal infrastructure patterns and then sharing them with the rest of your IaC practitioners is a way to make those lessons learned easy to consume and address the skills gap.
We also recommend leveraging custom Terraform modules for additional benefits, including the implementation of the following:
- Your naming convention.
- Your security controls.
- FinOps standards.
Before creating custom Terraform modules, it is essential to understand specific requirements:
- Custom Terraform module code must be hosted in a VCS repository.
- Terraform modules must follow the semantic versioning scheme.
- Follow the official module naming convention.
- Follow the standard module structure.
- Module publishing permissions.
- Configured VCS provider.
In the next subsections, we’ll cover those requirements in more detail.
Host custom Terraform module code in a VCS
HashiCorp recommends hosting Terraform module code in a supported Version Control System (VCS) repository. This allows for version control, which enables teams to track changes to code over time and revert to previous versions if necessary. It also facilitates collaboration, as team members can work on the same codebase and seamlessly merge changes. Finally, it provides a single source of truth for the codebase, allowing you to cut a release and publish it in the private registry.
Use Semantic versioning
HCL is designed to work with the semantic versioning scheme, as exemplified by the use of version constraints in the terraform
block for the terraform binary version, or the provider versions, and the module
block to set constraints on module versions.
HashiCorp recommends using the semantic versioning scheme so that your Terraform practitioners can take advantage of version constraints in the code and the available module publishing automation.
Follow the official module naming convention
HashiCorp has defined an official module naming convention, which is expected by the public and private registry. The naming convention is composed of three parts separated by a hyphen (-) character: terraform-<PROVIDER>-<NAME>
. Note that the <NAME>
segment may contain additional hyphens. Here are two examples of valid module names:
terraform-google-vault
terraform-aws-ec2-instance
Follow the standard module structure
The private registry expects the module structure to follow a certain pattern. This allows the registry to inspect the module and:
- Generate documentation
- Track resource usage
- Parse examples
- Parse submodules (if any)
- Execute the module test suite (if any and configured)
This information is then available to the practitioner and helps with module discovery and usage.
Module publishing permissions
Only members of the “Owners” team or members of a team that has been granted the organization-level “manage private registry” permission can publish (or delete) modules.
HashiCorp recommends setting up a team in TFC/E to delegate private registry management and not rely on the “Owners” team. If you need to integrate TFC/E with a CI/CD pipeline that will drive module publishing, we recommend using a team API token to allow the CI/CD pipeline to publish the module.
Configured VCS provider
You must have configured a VCS provider for the VCS system hosting your Terraform modules and provided administrative access to the repositories hosting the custom Terraform modules.
The procedure for configuring the VCS provider varies depending on the supported VCS system. Please see the online documentation for more information.
Publishing a module in the private registry
Once your module is written and all the requirements are met (i.e., host the code in a VCS, use semantic versioning, use the standard module naming convention and structure, and set up permissions in TFC/E), you are ready to publish the module in the private registry.
You will be prompted to select either the Tag or Branch module publishing option. Here is what you need to know about each option:
Tag-based | Ideal for modules associated with release tags in your VCS repository. The registry will automatically detect and publish new versions based on these tags. If your VCS supports it, we recommend considering implementing tag protection rules to prevent a malicious user from changing the association of a tag with a commit. |
---|---|
Branch-based | Provides enhanced flexibility, permitting the selection of a particular branch in your VCS and the assignment of a distinct version number to the module. Currently, this mode is also required to make use of the integrated testing capabilities of the private registry. |
Module publishing can also be done using the API and integrated into a CI/CD pipeline. See the documentation available online on our website.
Once a module has been created and published, you can switch to a module maintenance workflow, illustrated below.
Sharing custom providers
There are many reasons why an organization may choose to develop and maintain custom providers that can be broken down into two categories:
- Flexibility and customization
- Efficiency and collaboration
Flexibility and customization
- Extend Terraform capabilities: With custom providers, organizations can create custom resources and data sources specific to their infrastructure or cloud platforms. This extends the reach of Terraform beyond standardized offerings, allowing for tailored automation and management.
- Rapid prototyping and innovation: Develop and test internal provider prototypes privately before exposing them to the broader community. This facilitates experimentation and innovation without the pressure of public scrutiny.
Efficiency and Collaboration
- Reduced dependency fatigue: Avoid relying on externally sourced providers with unknown provenance or compatibility issues. Custom providers enable the consolidation of internal modules, enhancing reliability and simplifying dependency management.
- Centralized governance: Facilitate centralized governance and approval processes for custom providers and modules. This ensures consistency and minimizes risks associated with uncontrolled infrastructure configuration.
Foster collaboration and knowledge sharing within your organization by making internal best practices and custom providers readily accessible to team members in order to standardize the approach.
Custom providers enable development and management of internal-only resources making them a valuable asset for managing complex and evolving on-premises infrastructure needs.
Remember that the decision to utilize custom providers should be based on your specific requirements and security considerations. While they offer numerous benefits, careful planning and management are crucial to ensure their effective implementation and secure operation.
Publishing a custom provider to your private registry
Prerequisites
Prior to creating custom Terraform modules, it's important to be aware of certain requirements:
- Custom Terraform provider code must be hosted in a VCS repository.
- Terraform providers must follow the semantic versioning scheme. Terraform supports specifying version constraints for providers as long as the provider uses semantic versioning.
- Terraform providers must be written using the Go programming language.
- You need a GPG key: Provider releases must be signed with a valid GPG key.
- Provider publishing permissions (see Registry Administrator).
Publishing steps
Publishing a custom provider to the private registry involves several key steps outlined in this section.
In addition to the steps illustrated above, you should also:
- Add documentation
- Prepare documentation: Write clear and comprehensive documentation for your provider, including how to use it, available resources, and configuration options.
- Host documentation: You can host this documentation within the VCS repository, on an internal documentation site, or alongside the provider in the registry (if supported).
- Announce and educate
- Announce the availability: Inform all relevant stakeholders and potential users about the new provider.
- Provide training and resources: Offer training sessions, documentation, and support channels to help users understand and effectively use the new provider.
- Maintenance and updates
- Plan for regular updates: Keep the provider up to date with regular updates and patches.
- Monitor usage and feedback: Continuously monitor its usage, gather user feedback, and make improvements as necessary.
- Additional tips
- Change log: Maintain a change log to document all changes, enhancements, and fixes in each version.
- Security: Regularly audit the provider for security vulnerabilities and address them promptly.
Please refer to the publishing provider section of the documentation for additional information and detailed examples.
Sharing whitelisted public Terraform modules and providers
Public Terraform modules are modules available on the Terraform public registry. The private registry allows the creation of a short list of modules from the public registry that are made available to the users via the private registry.
Using the modules available in the public registry can be a cost-effective way of achieving objectives similar to those of a custom module because a third party does maintenance. However, it does come with responsibilities, as we recommend vetting those modules first, which will require effort on your part. This step cannot be overlooked as supply-chain security is an important component of your security posture, as high-profile security incidents have proven in the last few years.
Similarly to Terraform modules, providers from the public registry can be whitelisted and made available via the private registry. The same vetting requirement applies to providers as they are also part of your “software supply chain”.
Publishing public providers or modules to the private registry
Publishing public providers or modules in the private registry is a simple operation:
Publishing public providers or modules to an airgapped private registry
The above-mentioned publishing method can be used for TFC and TFE instances, but only if the TFE instance has access to the public Terraform registry. However, this may not always be possible due to organizational requirements, such as the need for increased security or if the TFE instance is located in a physical environment where internet access is limited or non-existent.
There is, however, a way to work around this obstacle for providers:
For additional information and detailed examples, please refer to the Publishing Public Providers in Airgapped Terraform Enterprise section of the documentation.
An alternative approach is to use the provider network mirror protocol. This method relies on:
- The providers have been identified, downloaded and placed on an internal hosting site.
- The Terraform CLI configuration file includes a directive to point to the network mirror URL for provider installation.
Because this method requires the provider_installation
directive in the Terraform configuration file, you will need to build and maintain a custom worker (or agent) image featuring this configuration.
For additional information and detailed examples about this alternative approach, please refer to the Provider Network Mirror Protocol and CLI Configuration File sections of the documentation.
Similarly, there is also a way around this obstacle for modules:
For additional information and detailed examples, please refer to:
Removing a public provider or module from the private registry
Deleting a public provider or module from your private registry does not remove it from the public Terraform Registry. Removing it from your private registry doesn't actually remove it from the public registry, so people in your organization could still find and use it. If you need to blacklist a public provider or module, you should implement a Sentinel policy that prevents Terraform runs that use a blacklisted provider or module.
Before proceeding with the deletion of a module or provider from your Terraform registry, make sure you understand how the deletion will affect your existing infrastructure. Here are the things you might consider to look for:
- Dependencies:
- Identify Dependent Configurations: Deleting a resource with active dependencies can disrupt infrastructure management and potentially lead to errors or unexpected behavior.
- Plan for Alternatives: If dependencies exist, consider strategies to address them before deletion. This might involve migrating to alternative providers or modules, adjusting configurations to eliminate reliance on the resource, or implementing a phased removal approach.
- Usage within workspaces:
- Review Workspace Configurations: Carefully inspect workspace configurations to confirm whether any active workspaces are currently utilizing the module or provider slated for deletion.
- Consider Impact: If in use, evaluate the potential implications of removal on those workspaces, including potential disruptions to infrastructure management or the need for configuration adjustments.
- Version control:
- Preserve Version History: Ensure you maintain a comprehensive history of the module or provider's versions within your version control system, even after deletion from the registry. This safeguards the ability to revert to previous versions or access historical configuration details if necessary.
- Documentation:
- Update Documentation: Meticulously update any organizational documentation or Terraform configuration examples that reference the removed module or provider. This maintains clarity and consistency, preventing confusion among team members who might reference outdated information.
- Communication:
- Notify Relevant Teams: Inform relevant teams and individuals within your organization about the removal, particularly those who might have been actively using the resource. This fosters awareness and allows for necessary adjustments to their workflows.
- Additional considerations:
- Alternative Access: Remember that deletion from the private registry doesn't preclude users from accessing the module or provider directly from the public Terraform Registry if needed.
- Security Implications: If removing a provider due to security concerns, promptly address any affected infrastructure components to mitigate potential vulnerabilities.
- Compliance Requirements: Adhere to organizational policies or regulatory compliance requirements that might mandate the retention of certain resources for auditing or record-keeping purposes.
Discovering Terraform Modules and Providers
Whether novice or expert, your Terraform practitioners benefit from an easy-to-use interface to the private registry instance. The discovery of Terraform modules and providers is done from the TFC interface by selecting the Registry entry in the left panel.
The web interface is designed to speed discovery. It features tabs to select either the “Modules” or the “Providers” view.
The “Modules” view features a flexible filter that allows users to select modules by provider, source (from the public or private registry), and publishing mode (tags-based or branch-based). The list can also be restricted to show only “no-code ready” modules.
The web interface allows quick access to a number of useful information about a module, such as:
- The module’s README documentation
- Input variables
- Output variables
- Dependencies
- Resource types created
- Configuration snippet (which can be copied into the clipboard in a single click)
- Module download statistics
- The Terraform Configuration Designer
The “Providers” view is simpler and provides a paginated list of available providers.
The web interface allows quick access to a number of useful information about a module, such as:
- Module documentation
- Provider download statistics
- Source VCS repository
- Configuration snippet (which can be copied into the clipboard in a single click)
Consuming Terraform Modules or Providers
Consuming Terraform modules or providers is relatively simple:
- Find the relevant module or providers in the private registry.
- Use them in your Terraform configuration
As a user of Terraform modules and providers, you should use version constraints because they are an easy way to avoid breaking changes that could happen if you always use the latest version. But you should also:
- Monitor the modules and providers you are using for deprecation notices.
- Set aside time to upgrade to a newer version of the module or provider before its deprecation is effective.
- Volunteer applicable feedback and lessons learned to the module or provider author(s) so that they can be improved for your use cases.
Module development and maintenance
Now that we’ve covered how to share, discover, and consume modules using the private registry, we must cover a few key points about module development and maintenance:
- Module testing
- Module version lifecycle
Module testing
With Terraform v1.6.0, HashiCorp introduced the terraform test
command and the official test framework for Terraform code. Since then, we’ve made continuous improvements to the framework, adding mocking capabilities in v1.7.0, for example.
We consider that Terraform modules should have a test suite and that the test suite should be maintained to match the module’s features. Our recommended module publishing high-level workflow includes testing steps, as highlighted in the diagram below.
For example, if you advertise to your practitioners that the module will deploy a virtual machine instance in one of the authorized regions, then you will want to:
- Expose variables to take the target region as an input.
- Validate that the specified region is a valid one and return a useful error message if it’s not.
- Have a test suite that validates that given a valid region as input, the module can deploy a virtual machine. The test suite will also ensure that given an invalid region as input, the module will not deploy the virtual machine.
The advertised features of the module are the “contract” between the module author and the Terraform practitioner. As a module author, you want to ensure that as you add more features to the module or fix bugs, you don’t break the contract.
As the features supported by the module grow, and the contract grows with it, it becomes more and more costly for the module author to check manually that changes don’t break the contract. While maintaining the test suite does represent more work, it’s easy to realize that it will pay dividends and is still a more efficient alternative to sticking with manual checks.
Terraform’s automated testing capabilities are a robust and efficient way to ensure that the contract will not be broken by mistake.
Module authors can implement two types of tests using the Terraform test framework:
- Unit tests
- Integration tests
We’ll go into more detail in the rest of the section.
Unit testing
Unit tests verify individual resources and configurations for expected values. They focus on things that are known at plan time:
- Model different combinations of input values
- Verify that conditionals/logic are working as expected
- Validate string interpolations
- Catch unintended consequences of refactoring
They also focus on things that should fail when fed invalid input:
- Input validation
- Preconditions
- Checks
For more information, see the following documents:
- Terraform v1.7 adds test mocking and config-driven remove
- Input Variable Validation
- Preconditions and Postconditions
- Checks with Assertions
- Expecting failures
- Terraform test Assertions
Integration testing
Integration tests verify that a configuration using the Terraform module and valid inputs is able to deploy the configuration successfully. They focus on things that are only known after the Apply:
- Verify expected values for calculated attributes & outputs
- Apply-time errors such as:
- Missing permissions
- Features or APIs not enabled in your account
- Invalid YAML/JSON in a policy or job spec
They also focus on interoperability testing:
- Validate the impact of a new
- Terraform version
- Provider version
- Child module version
- Detect inconsistencies between cloud regions and partitions
For more information, see the following documents:
Selected examples
We’ll present examples of the following test techniques:
- Input validation
- Pre and postconditions
- Check
Input validation is about specifying a custom validation rule for a particular variable. The example below validates that the AMI ID has the correct format.
variable "image_id" {
type = string
description = "The id of the machine image (AMI) to use for the server."
validation {
# regex(...) fails if it cannot find a match
condition = can(regex("^ami-", var.image_id))
error_message = "The image_id value must be a valid AMI id, starting with \"ami-\"."
}
}
Pre and postconditions are used with resources, data sources and outputs. Terraform checks a precondition before evaluating the object it is associated with and checks a postcondition after evaluating the object. Terraform evaluates custom conditions as early as possible but must defer conditions that depend on unknown values until the Apply phase.
The example below shows a precondition. In this scenario, the code author wants to ensure that the AMI architecture used to deploy the EC2 instance is of type arm64
.
resource "aws_instance" "this" {
instance_type = "t3.micro"
ami = data.aws_ami.this.id
lifecycle {
# The AMI ID must refer to an AMI that contains an operating system
# for the `arm64` architecture.
precondition {
condition = data.aws_ami.this.architecture == "arm64"
error_message = "The selected AMI must be for the arm64 architecture."
}
}
}
The condition will validate that the AMI is of the correct architecture type, and if it’s not, Terraform will throw the error_message
.
The example below shows a postcondition. The code retrieves an aws_ami
using a data source. The data source block features a lifecycle
block and a postcondition
block, which validates if the AMI ID has the required tag.
data "aws_ami" "this" {
id = var.aws_ami_id
lifecycle {
# The AMI ID must refer to an existing AMI that has the tag "tfe-server".
postcondition {
condition = self.tags["Component"] == "tfe-server"
error_message = "tags[\"Component\"] must be \"tfe-server\"."
}
}
}
The condition
will validate if the object referenced using the keyword self
has a Component
tag set to tfe-server
. If that is not the case, Terraform will throw the error_message
.
The check block
validates your infrastructure outside of the usual resource lifecycle. It uses one or more assert blocks to perform validations. The example below performs a GET request on the Terraform website and throws the error_message
if the returned status_code
is anything but 200.
check "health_check" {
data "http" "terraform_io" {
url = "https://app.terraform.io/"
}
assert {
condition = data.http.terraform_io.status_code == 200
error_message = "${data.http.terraform_io.url} returned an unhealthy status code"
}
}
Terraform Validation Options Summary
We’ve presented a number of strategies to perform validations in Terraform. The table below lists the options and their characteristics so that you can select the best option for the job.
Capability | Version | Audience | Blocking? | Scope/Lifecycle |
---|---|---|---|---|
Input validations | >= 0.13 | Consumer | Yes | Input variables |
Pre/postconditions | >= 1.2 | Consumer | Yes | Resource, data source, output |
Checks | >= 1.5 | Consumer | No | Terraform configuration, post-plan/apply |
Tests | >= 1.6 | Producer | No | Modules, separate from plan/apply |
Policies | TFC/E | Consumer | Optional | All in-scope provisioning runs - all resources, plan, state, and run data |
Test-integrated module publishing
With the introduction of the Terraform test framework, HashiCorp also rolled out the “test-integrated module publishing” feature in HCP Terraform. The objective of this feature of the private registry is to streamline the module testing and publishing process.
The test-integrated module publishing option is available for the branch-based module publishing option. Once enabled on the module, test runs will execute automatically based on version control events such as pull requests and merges. They can also be initiated from the command line or using the API.
In addition to the improved integration with pull requests and merges, this feature allows tests to execute remotely in a secure environment, eliminating the need for developers to handle sensitive cloud credentials on their workstations.
For more information, see the following document: Test-integrated modules in the HCP Terraform private registry.
Module version deprecation
As you release new module versions, you may need to deprecate versions for a few reasons:
- The module author(s) have discovered a critical issue with a version, and your Terraform practitioners should not use that version due to its critical nature.
- The module author(s) may EOL older versions that are no longer supported. To strike a balance between adequate support and available resources, you may have an internal policy to only support the latest n-2 versions of a module. All versions older than that would then be “unsupported” and therefore deprecated.
While there are legitimate reasons to deprecate a module version, module authors must consider the needs of their stakeholders, which in this case are the teams using those module versions. To minimize disruption and ensure a smooth transition, we recommend a number of practices to handle module version deprecation:
- Be transparent and communicate
- Make it easy to transition to a supported version
- Manage the post-deprecation lifecycle
- Support your stakeholders
Transparent deprecation announcement
- Early communication: Clearly announce the deprecation of a module version well in advance of its end-of-life (EOL) date. Utilize readily accessible channels such as documentation, release notes, and dedicated communication platforms to reach all users.
- Detailed timeline: Establish a clearly defined timeline for the deprecation process. Include milestones such as the announcement date, EOL date, and any interim points for limited support or resource availability.
Facilitate the transition
- Provide migration paths: Offer well-documented alternatives for users looking to migrate from the deprecated version. This can include newer versions of the same module, compatible modules from diverse sources, or even entirely different approaches to achieving the desired outcomes.
- Develop migration guides: Create comprehensive, step-by-step guides to accompany each migration path. These guides should address common scenarios, potential complications, and best practices for transitioning configurations.
Manage the post-deprecation lifecycle
- EOL enforcement: Upon reaching the EOL date, use Sentinel policies (advisory) to flag Terraform code that still uses deprecated modules and inform the user. Consider removing the module version from all public registries or repositories to prevent inadvertent access and deployment.
- Preserving knowledge: Maintain the deprecated version's documentation for reference purposes. This will aid existing users still within the migration window and facilitate support during the transition period.
Support your stakeholders
- Limited support window: Extend limited support for the deprecated version during the transition period. Focus on addressing critical bugs and issues that might impede ongoing operations.
- Open communication channels: Establish effective communication channels to answer user questions, address concerns, and gather feedback during the deprecation process. Consider hosting discussions, Q&A sessions, or dedicated support channels to facilitate information exchange. Open communication with your internal customer base cannot be understated.