Unlock the secrets of efficient infrastructure management β
# General π§©
HashiCorp Terraform has become a go-to choice for “infrastructure as code” (IaC) enthusiasts, providing a powerful and flexible way to define, manage, and provision infrastructure resources, not only on Microsoft Azure! But as your infrastructure codebase grows, keeping it organized, maintainable, and scalable can be a challenge. π
That is where modularization comes into play. In this blog post, I will explain why modularization is crucial in Terraform and show the best practices πfor creating and developing efficient Terraform modules (“module library“).
# Why ‘Modularization’ in Terraform? π¦
Modularization in Terraform involves breaking down your infrastructure code into smaller, reusable components called ‘modules’. π¦π¦π¦
A module encapsulates specific functionality or resources and allows you to manage your infrastructure in a more organized and standardized manner. There are several compelling reasons to embrace modularization in Terraform:
1. Do one thing and do it as simply as possible π€
Modularization encourages the creation of small, self-contained and independently testable modules that each serve a specific purpose. This approach follows the philosophy of “doing one thing and doing it well“. By breaking your infrastructure down into discrete, specialized modules, you enhance clarity, maintainability, and reusability. Keep your modules focused on a single concern. If a module starts doing too much, consider breaking it into smaller, more specialized modules.
π’ Every single module is a separate repository! β‘οΈ Do not create ‘monster‘ πΉ repos containing multiple modules! π€’
Furthermore, avoid creating “thin wrappers“, which refers to modules that merely wrap or pass through the arguments and resources directly to underlying resources without providing much abstraction or additional functionality. A Terraform modules that contains only one single resource does not bring any additional value. It is generally recommended to design Terraform modules with a balance of abstraction, encapsulation, and documentation to create reusable and maintainable infrastructure code! π
β
For example, before start programming a Terraform module to provision Azure Virtual Network (VNet) for Hub & Spoke, do a list which resources should be covered in the module. Ask yourself: “What characterizes an Azure VNet?” and “How the module should look like so it can be reused by several organizations, departments or workloads?“. The following example shows which resources are covered in my VNet module. Of course, yours can look different according to the given requirements.
2. Standardized Module Structure π·ββοΈ
The standard module structure for Terraform, described by a file & directory layout, recommends the following elements:
The following link on my GitHub account can be used as a module template. Feel free to use it! π
Provide a clear and standardized naming conventions for all module before you start programming. Use consistent and descriptive names for the modules and its variables makes it much more recognizable.
3. Provide options, but not too many ποΈ
Terraform modules should be flexible but not overwhelming. Offering a reasonable number of input/output variables and options gives users the ability to customize their infrastructure while avoiding unnecessary complexity.
β
For example, the previous screenshot is the list of resources that the module may create, but do not have to! The module can create zero or more of each of these resources depending on the ‘count‘ value (Link) or the ‘for_each‘ loop (Link). The ‘count‘ value is determined at runtime. The goal is to present the types of resources that may be created. It makes sense to provide the consumers of the Azure VNet module the option, either to create an Azure VNet (count = 1) or use an already existing VNet (count = 0) to configure additional settings on it like subnets, NSGs, route tables, etc.
4. Add value, do more than wrap a resource β
A good Terraform module should do more than just wrap a single resource or component. It should add value by simplifying complex configurations, providing sensible ‘default‘ values, and offering a higher-level abstraction to make infrastructure management easier.
β
For example, a module for an Azure Storage Account should provide standard values for the most common infrastructure scenarios, like the following screenshot illustrates. If the variable is not defined in the ‘.tfvars‘ file, the default value will be used automatically. This reduces the number of variables which are needed to define the configuration for the Azure Storage Account and reduces complexity.
5. Version appropriately π
Versioning ensures consistency and allows users to depend on specific module versions. Use semantic versioning (‘SemVer‘) to communicate changes clearly, and always document what each version brings to the table. A version number is typically written as ‘MAJOR.MINOR.PATCH'
. When making changes to a module, update the module’s version according to ‘SemVer‘ and use tags on the repository. After updating the version with the new tag (as the following screenshot illustrates), consumers can choose when to upgrade to the new version in the main configurationπ.
6. Create great documentation π
Documentation is vital for module adoption. Clear and comprehensive documentation should explain the module’s purpose, usage, input variables, outputs, and any caveats. π Well-documented modules are easier for others to understand and use. π‘ Providing concise descriptions in ‘variables.tf‘ and ‘outputs.tf‘ for documentation purposes is mandatory; same for the usage of the ‘examples/‘ in the subdirectory.
β
The following example, shows a description, defined in the ‘README.md‘ file to know more in detail what the module is about and can be used for.
7. Keep them updated π
Regularly update your modules to incorporate new features, bug fixes, and security patches. Keeping modules ‘up-to-date‘ is crucial for maintaining the health and security of your infrastructure. Follow the principal “Don’t get left behind!” with your module versions, using old providers. Keep in mind π§ that cloud is an ‘evergreen‘ approach. π³
β
For example, always keep an eye ποΈ on new AzureRM providers. To find the latest information on updates, release schedules, and upcoming releases for the Terraform AzureRM provider, I recommend checking the official sources. Typically, this information is available on the official Terraform AzureRM provider GitHub repository. π
8. Evangelize & Engage π’
Promote your Terraform modules within your organization across different departments/subsidiaries or the broader community. π§ Teams across an organization can collaborate more effectively when they share a common Terraform library of modules (called ‘registry‘). This collaboration can lead to knowledge sharing, improved workflows, and faster development cycles. Different teams can easily reuse existing, tested modules for common infrastructure patterns. Moreover, a centralized/standardized library allows for the incorporation of best practices and industry standards. Modules can be designed to follow security, compliance, and performance guidelines, ensuring that infrastructure is deployed in a secure and efficient manner (‘governance‘). Furthermore, collaborate with others and conduct code reviews to identify potential issues and improvements. ποΈ
β
For example, using a central Terraform library as registry helps organizations manage their infrastructure more efficiently and in accordance with best practices. The following chart below shows the main principal of collaboration within an organization.
9. Start simple and evolve π
Begin with a simple, functional module and then evolve it based on feedback and changing requirements. Starting simple allows for rapid iteration and improvement. Regrading modules, you can either strive for feature completion or start with a minimum set of needed arguments:
10. Make them easy to obtain π
Modules should be easy to discover and obtain. Consider using Terraform Module Registries to simplify module distribution and discovery.
β
For example, using Terraform Cloud as a central registry for publishing modules is a common and effective approach in ‘infrastructure as code‘ (IaC). The Terraform Cloud provides a centralized location to store, publish and version your Terraform modules, making it easier to collaborate, share, and manage infrastructure code across teams.
11. Testing & Validation: π§ͺ
Thoroughly test your modules to ensure they work as expected in various scenarios. Testing Terraform modules is an essential part of ensuring their reliability and correctness (Link). By combining the following testing approaches, you can create a robust testing strategy for your Terraform modules, helping you catch errors early and ensure the reliability of your infrastructure deployments:
# Conclusion π
Terraform modularization is a fundamental technique for managing infrastructure as code (IaC) effectively. By adhering to the best practices outlined in this blog post, you can create and develop Terraform modules that are understandable, reusable, and maintainable. Modularization not only simplifies infrastructure management, but also encourages collaboration and ensures your infrastructure codebase remains robust and adaptable as it grows and evolves.
#HappyCoding π #AzureRocks π€#IaC π₯οΈ