Over the last few months I have been writing a lot of Terraform code. One of the main issues that I’ve been dealing with is how to break apart modules. For example, I wrote a module for deploying an Application Load Balancer. In addition to creating an ALB, I also create listeners, listener rules, DNS entries, security groups, and certificates. It seemed easy enough when I started out, but as I progress, and requirements changed, I started running into issues. What happens when I need to add a non-standard port listener (something like 8443 instead of just 80 or 443)? What about needing a listener rule that used a different, non-AWS generated certificate?
As these changes were introduced, the code became more convoluted, with a lot of conditionals and local variables. I did not even have a good way to create new listeners without just adding more code for each specific listener type, which just feels dirty. I could break each of them up into seperate, individual modules, but they still feel like they went together. I didn’t want to manage four or five individual modules and the repositories that go with them, especially since they all depend on each other. That’s why I was excited to find out about sub-modules.
While I was doing some research aroound various testing strategies in the Terraform Registry I started noticing something called sub-modules while browsing various repositories. Digging in more deeply, I found that you could now add a single module to the registry that has multiple sub-modules and then call them directly from Terraform. As I started to dig deeper, I found that some allowed the parent module to do some piece of work while others only had individual sub-modules.
With that as a starting place, I decided to rewrite my ALB module to follow this new pattern. I broke my module up into five sub-modules:
├── modules
│ ├── alb-certificate
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ ├── variables.tf
│ │ ├── versions.tf
│ │ └── README.md
│ ├── alb-dns
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ ├── variables.tf
│ │ ├── versions.tf
│ │ └── README.md
│ ├── alb-listener-rule
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ ├── variables.tf
│ │ ├── versions.tf
│ │ └── README.md
│ ├── alb-listener
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ ├── variables.tf
│ │ ├── versions.tf
│ │ └── README.md
│ └── alb
│ ├── main.tf
│ ├── outputs.tf
│ ├── variables.tf
│ ├── versions.tf
│ └── README.md
├── .gitignore
├── .pre-commit-config.yaml
├── LICENSE
└── README.md
This allowed me to break the code up into smaller chuncks while keeping it all together. I can call the individual submodules as need like this:
module "https-listener" {
source = "AustinCloudGuru/alb/aws//modules/alb-listener"
version = "1.0.2"
load_balancer_arn = module.alb.alb_arn
security_group_id = module.alb.security_group_id
cidr_blocks = ["0.0.0.0/0"]
port = "443"
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-2016-08"
certificate_arn = module.default-certificate.arn
}
I’m really excited about the opportunities that this pattern gives me. I don’t like having a lot of different but interdependent modules and this solves that problem for me. I’m really looking forward to revisiting some of my other modules.
Comments