Source & Binary

Writing Terraform code with an assist from Substrate

Substrate generates a directory structure for your Terraform code, remote state file configurations, provider configurations, and module blocks so you can get started right away writing Terraform code that matters to your business.

Directory structure

tl;dr: Write code in the modules subdirectories Substrate creates for each domain. Run Terraform from the root-modules subdirectories matching the domain, environment, quality, and AWS region you want or just use substrate create-account.

Suppose you’re responsible for a service called example that you run in staging as alpha-quality and in production as beta- and gamma-quality. You will create those environments and qualities interactively with substrate bootstrap-network-account and will then run the following commands to create all your service accounts:

These will create the following directory trees, which you will should commit to version control:

What do you do next? And where do you do it?

The vast majority of your work should happen in your domain’s Terraform modules. In this example domain those are modules/example/global and modules/example/regional. Put global resources like IAM roles and Route 53 records in modules/example/global. Put regional resources like autoscaling groups and EKS clusters in modules/example/regional. (Substrate has generated all the module blocks necessary to instantiate these modules with the right Terraform providers.)

You selected some number of AWS regions when you configured your network but you may not want to run all your infrastructure in all those regions all the time (if for no other reason than cost control). You may edit root-modules/*/*/*/*/ to customize which of your selected regions are actually in use. By default, all your selected regions are in use. If you don’t want to provision your infrastructure in any of them, simply comment out the resources in root-modules/*/*/*/*/ (substituting your domains, environments, qualities, and regions as desired).

Testing and deploying Terraform changes

It’s no accident that modules/example/global and modules/example/regional are referenced by the root Terraform modules for every environment and quality in the domain. These afford you multiple opportunities to implement changes in pre-production and partial-production in order to catch more bugs and failures before they impact all your capacity and all your customers. Continuing from the example above, here is the complete lifecycle of a Terraform change in the example domain:

  1. Change e.g. an EC2 launch template in modules/example/regional/
  2. Commit, push, get a code review, and merge into the main branch
  3. substrate create-account -domain example -environment staging -quality alpha and verify your changes
  4. substrate create-account -domain example -environment production -quality beta and verify your changes, either at the end or at each pause between regions
  5. substrate create-account -domain example -environment production -quality gamma and verify your changes, either at the end or at each pause between regions

Referencing Substrate parameters in Terraform

As you write your own Terraform modules, you’re certainly going to want to parameterize them in the same ways Substrate helps you parameterize your AWS accounts. Plus, you’re also going to need a network, and Substrate’s already created some and shared the right one with every service account to make it easy, secure, and cost-effective to build new things.

substrate create-account automatically creates global and regional Terraform modules for your domain in modules/domain. Those modules include a reference to modules/substrate which provides the following helpful context: