When it comes to writing Terraform modules I find it's an extremely common practice, in most organisations, to produce a module that wraps a single
resource. The end result is a module that prevents a (crap) API in front of an existing, much better API.
Let's look at an example: the AWS Security Group.
Here are the arguments that can be past to a
aws_security_group (at the time of writing):
description - (Optional, Forces new resource)
egress - (Optional, VPC only)
ingress - (Optional)
name_prefix - (Optional, Forces new resource)
name - (Optional, Forces new resource)
revoke_rules_on_delete - (Optional)
tags - (Optional)
vpc_id - (Optional, Forces new resource)
At a recent engagement I noticed a Terraform module that wrapped the
aws_security_group type. It did two things of note:
- It defined
- It did not have an option for
There are three problems with
- Don't use
security-groupin the name for a Security Group - I know it's a Security Group already
- You've forced me to use a naming convention I might not want to use (
- A change to this field results in resources being recreated might can cause issues for some
There is a two issues with
- I cannot provide the module with this value
- I now have to use the
resourcedirectly, breaking away from the module, and going outside of expected practice
All of these issues compound into a single set of issues:
- Technical debt
- Fragile API
- Do I upgrade?
Each module of this nature contains hidden technical debt just waiting to bite consumers of your modules.
Of course I'm now blocked whilst I wait for you to make the change to your module's API to include the options that I need.
Your API is fragile. If the underlaying resource changes its API yours is a bad, broken API in an instant.
Do I Upgrade?
And of course if you do make the changes needed to support some request from me, other consumers are now asking themselves, "Do I upgrade?" This creates a task in someone's backlog/sprint that they have to resolve, all because you wrapped a single
resource in a module.
When do I write a module?
I have some basic rules that aren't hard line or perfect, but they've served me well:
- Are you wrapping at least two or more distinct
resourcetypes in the module?
- Does you module build something complex, i.e. "My module will create an Route53 record, then request and validate an ACM TLS certificate for that record, create an ALB with a listener and tie the TLS cert to TCP/443"... that's complex.
- Are you creating a module for something complex that's repeated at least three times elsewhere in other environments?
Sometimes these rules don't work "out of the box". Fine. Just adjust them, but please...
Stop wrapping individual resources in a module.