I’ve recently spent some time going over the configuration of my personal infrastructure (including this blog, various bits of Lambda code, tidyrisk, and whatnot). Between all the changes in the AWS landscape, the rapid rise of GitHub Actions, alternatives to Docker Hub for public container image publishing, and HashiCorp issuing three major Terraform releases over the past 18-24 months, there was a lot of work to be done (and still more to do). This is an initial review on how I’m managing my personal infrastructure as code (IaC) setup. These are very similar (often identical) practices to how I approach things in a work environment, so I hope they prove useful to others.
All Hail the Mono Repo
One of my biggest changes in thinking has been on my structuring of code. After several years of aggressively creating repositories for every modular bit of IaC code I wrote, I’ve come back to the simplicity of a mono-repo. The overhead of managing dozens of different micro-repositories, plus all of the version changes required to pull in new versions to the main repository was unsustainable. My current top level structure has three folders:
- components - discrete, stand-alone chunks of infrastructure, templatized for reuse
- main - currently deployed components and their supporting variables
- modules - repeatable chunks of infrastructure that are consumed by components, but not meant to stand alone outside of a component
Components can be thought of as projects. This blog, for instance, is a single sub-folder in my components directory. Projects may come and go, with some projects no longer being deployed while others being replicated across multiple dev and prod environments. The deployment of these components is handled in the main directory (formerly titled master). In the main directory is a reference to every active and deployed project, with the variables that set the correct environment and other deployment specific variables. With this structure I can make a change in a project and ensure that any active deployments get those changes. Finally, projects can consume modules of helper infrastructure. When these are modules I’ve written myself (more on that shortly), they live in the modules directory.
When deploying a change (or just verifying current state), all I need to do is
go into the
main directory and run a
terragrunt plan-all – I’ve not yet
felt the need to go to something like Atlantis or
Terraform Cloud – to check
on the deployment of my entire infrastructure stack. Between this shift in
to the mono-repo structure and the module changes below, I’ve
retired close to 20 different private code repositories. This makes maintaining
code much easier.
A Word on Modules
Terraform has a rich community providing open modules and reusable functionality. My use of modules from others has ebbed and flowed, ranging from writing my own code for everything (full control, but lots to maintain) to using as many modules as possible (less to maintain, but tracking dependencies and auditing others code is its own burden). At the moment, I’ve found a sweet spot where I’ll use the official HashiCorp partner modules for core functionality like VPCs, etc. and a limited number of third-party modules from a small number of authors (almost exclusively CloudPosse.
GitHub’s built-in Dependabot nominally has Terraform support, but it’s limited to the point of being unusable. There’s a community fork that works better on Terraform 0.12+ deployments, though it has its own problems with some syntax parsing. I’ve been able to hack around most of the warts at the moment. Longer term, I may need to switch over to Whitesource Renovate for more robust support if GitHub’s own Dependabot doesn’t get some love.
I simply adore pre-commit. With a single YAML file in the root of each repositories, I ensure that otherwise ephemeral git pre-commit hooks are available to run all sorts of language-specific sanity checks on code before committing. For Terraform repositories, I use six checks from this source. Two are format linters for Terraform/Terragrunt code, two perform syntax validation, one generates README documentation blocks ensuring that inputs, outputs, and dependencies are always documented, while the final is a recent add of Checkov for static security code analysis.
It’s been exciting to see a static code analysis tool for IaC code, especially an open one such as Checkov. As a team of one for my personal work, having another set of eyes on code configuration is a handy sanity check. Sure enough, Checkov found a couple of warts in my set up that, while not concerning, weren’t optimal (why had I ever assigned IAM policies directly to users instead of groups?).
There’s quite a bit more to say on tracking configuration drift, migrating CI/CD proceses, and other topics. Stay tuned!