monotf

module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 16, 2024 License: MIT

README

monotf

monotf is a tool for managing monorepo(s) of Terraform workspaces. monotf provides a centralized way to manage Terraform versions and to run Terraform commands across multiple workspaces. Upstream terraform requires the purchase of Terraform Enterprise / Cloud for central state summarization, management, and workspace queuing - monotf provides a free alternative which can be run in any workflow tool.

Terraform & Monorepos

Terraform is a great tool for managing infrastructure as code. However, it is not designed to be used in a monorepo, as the number of resources grows it slows down the terraform execution. This is because terraform must load all of the resources in the monorepo before it can execute any commands. This is a known issue, and there are some workarounds, but they are not ideal.

However when centrally managing platform resources, a monorepo can be useful to centrally roll out changes across multiple environments. For example, if you have a platform which is deployed across multiple AWS accounts, you may want to make a change to the platform and roll it out to all of the accounts. A monorepo is a great way to do this, as you can make the change in one place and then roll it out to all of the accounts.

monotf aims to provide "the best of both worlds" - you can create a mono-repo structure, while isolating execution boundaries at specifed levels - and of course not having to pay for Terraform Enterprise.

Terraform Backend

monotf does not manage terraform state. Instead, you must configure a supported backend for state management. monotf simply manages the workspace meta configuration.

As outlined in the Terraform documentation, it is not recommended to store sensitive configuration in the backend block. Instead, you should store your backend config (such as your pg connection string) in a secure location and make it available as an environment variable at runtime. This is discussed in the Environment Variables section below.

Usage

monotf is a command line tool. It is designed to be used in a CI/CD pipeline, but can also be used locally. It enables decentralized / distributed workflow execution (such as in GitHub Actions) while maintaining a centralized configuration and queueing system.

usage: monotf [flags] <command> [args]
  -addr string
        monotf server to use
  -config string
        path to config file (default "monotf.yaml")
  -dir string
        path to repo directory
  -init
        initialize repo (default true)
  -log-level string
        log level (default "debug")
  -port int
        port to run server on (default 8080)
  -vault-addr string
        vault address
  -vault-namespace string
        vault namespace
  -vault-path string
        vault path
  -w string
        workspace to use
  -wait string
        timeout for waiting for workspace to be ready. 0 means no timeout (default "0s")
commands:
  sys-init
  server
  terraform
  terraform-speculative-plan
  terraform-plan-apply
Commands
sys-init

Initialize the system with the supported binaries. This can be called as part of a container build to pre-cache the binaries.

server

Run the monotf server. This is used to store workspace metadaata and provide a basic queueing system for workspace executions. Note that the server does not manage state, that is managed by the Terraform backend. Also note that the actual terraform code execution does not happen on the server (as it does with Terraform Enterprise), instead the server simply manages the queue and provides a way to execute the code in a distributed fashion.

terraform

Run a terraform command in a workspace. This command will queue the workspace and wait for it to be ready before executing the command.

terraform-speculative-plan

Run a speculative plan in a workspace. This command will queue the workspace and wait for it to be ready before executing the command. This command is useful for running a plan in a workspace without actually applying it. This can be used to check for errors in the code, or to check for drift in the infrastructure.

terraform-plan-apply

Run a plan and apply in a workspace. This command will queue the workspace and wait for it to be ready before executing the command. This command is useful for running as part of an auto-merge workflow, where you want to run a plan and apply in a workspace after a PR is merged.

Configuration File

monotf is configured using a yaml file. The default location for this file is ./monotf.yaml, but you can specify a different location using the -config flag.

Example configuration file:

# directory in which to store terraform binaries
bin_dir: /tmp/bin
# supported terraform versions
versions:
- 1.5.7
- 1.6.6
# default terraform version if not specified in
# the workspace with a .terraform-version file
default_version: 1.6.6
# org name, useful when using a common remote state backend
# this will be used to create a unique workspace name
org: testing
# the monotf server address
server_addr: http://localhost:8080
# the directory in which the terraform code is stored
dir: "test"
# optional: infer environment variables from the path of the workspace
# for example, if you store your code in "test/aws13" then you can
# infer the AWS_PROFILE environment variable from the workspace path
path_template: "{{AWS_PROFILE}}"
# optional: a script which will be run in the workspace directory
# to set environment variables for the terraform shell
var_script: "$PWD/test/vars.sh"
# optional: retrieve environment variables for the terraform shell
# from a vault secret. For enterprise vault, you can specify a namespace
vault_env:
  addr: https://vault.example.com
  namespace: ""
  path: "kv/myapp/env"

Terraform Workspace Name

The terraform workspace name is generated by combining the org with the workspace path relative to the dir, where slashes (/) are replaced with hyphens (-). For example, if you have the following directory structure:

my-repo-name
├── aws01
│   ├── us-east-1
│   │   └── main.tf
│   └── us-west-2
│       └── main.tf
├── aws02
│   ├── us-east-1
│   │   └── main.tf
│   └── us-west-2
│       └── main.tf

And you have the following configuration:

org: my-org
dir: my-repo-name

Then the following workspaces will be created:

my-org-aws01-us-east-1
my-org-aws01-us-west-2
my-org-aws02-us-east-1
my-org-aws02-us-west-2

Environment Variables

monotf makes uses of environment variables to configure the terraform workspace. The goal is to make your terraform structure as declarative as possible, and let the structure of the workspace determine the configuration. In addition to the existing system environment variables, monotf will use the following methods to set environment variables:

Path Template

The path_template configuration option allows you to infer environment variables from the path of the workspace. For example, if you store your code in my-repo-name/aws01/us-east-1 then you can infer the AWS_PROFILE and AWS_REGION environment variables from the workspace path.

For the example above, you would configure the following:

path_template: "{{AWS_PROFILE}}/{{AWS_REGION}}"

Then, when you run monotf commands in the aws01/us-east-1 workspace, the following environment variables will be set:

AWS_PROFILE=aws01
AWS_REGION=us-east-1
Var Script

The var_script configuration option allows you to run a script in the workspace directory to set environment variables for the terraform shell. This is useful if you need to set environment variables which can be inferred from the workspace context.

The script can be written any language that can be run as an executable. A MONOTF_ENV environment variable will be available in the script's environment. The script can write any environment variables to MONOTF_ENV in the format VAR=value.

The script is run in the workspace directory, so you can use relative paths to access files in the workspace. Additionally, inferred environment variables will be available in the script's environment.

For example, assuming you set the above path_template configuration, you could use the following var_script to set a TF_VAR_role_arn variable which can be used to assume a defined role:

#!/bin/bash
account_number=$(aws sts get-caller-identity --profile $AWS_PROFILE --query Account --output text)
echo TF_VAR_role_arn=arn:aws:iam::$account_number:role/TerraformIAM >> $MONOTF_ENV

Note that it is using the AWS_PROFILE environment variable which was inferred from the workspace path to run a command to then set the TF_VAR_role_arn environment variable.

Note: make sure your script is executable (chmod +x).

Vault Environment Variables

Assuming you are using a backend which requires some secret parameters to access, you will need to specify those as TF_VAR_ environment variables. This can be done out of scope of the terraform, but it is sometimes difficult to remember / configure appropriately. If you store your variable(s) in HashiCorp Vault, you can use the vault_env configuration option to retrieve them automatically.

You will need to set the VAULT_TOKEN env var first - that is still out of scope of monotf. Then, you can configure monotf to retrieve the variables from Vault before running the terraform.

Server Deployment

Monotf operates in a client/server model. The server is used to store workspace metadata and provide a basic queueing system for workspace executions. The client is used to execute terraform commands in the workspace.

Both client and server are available as precompiled binaries as well as docker images. The binaries are available on the releases page. The docker images are available on Docker Hub.

Docker Image

The docker image available in DockerHub only has the monotf binary, curl, unzip, sh, and a few other packages avaiable in the alpine base image. In practice you will probably find the need for additional binaries in your workflow, so you will probably want to create your own image based on the monotf image, or by downloading the precompiled binaries and adding them to your own image.

Kubernetes

See manifests for example kubernetes yaml files for deploying the server. Note this only creates a ClusterIP service, you will need to expose it as appropriate for your environment. Make sure you fill in your secret values in the yaml files before deploying. Obviously at scale you would probably be using something like external-secrets.io to manage this.

Database Backend

The server relies on a database backend to store workspace metadata. Currently the server supports PostgreSQL and SQLite. The database backend is configured using environment variables. The following environment variables are supported:

Variable Description Driver Support
DB_DRIVER The database driver to use. Currently supported: sqlite, postgres All
DB_PATH The path to the database file. sqlite
DB_HOST The database host. postgres
DB_PORT The database port. postgres
DB_USER The database user postgres
DB_PASS The database password postgres
DB_NAME The database name postgres

Repository Set Up

Once the server is up and running, you can add new monorepos to it. You will need to create a monotf.yaml file in the root of the repo. See the Configuration File section above for details on the configuration options. You can use multiple monotf.yaml files in a single repo, but they must either be in different directories, or you must pass the -config flag with the path to the config file.

It is recommended to set the org to the repo / "resource vertical" name, but it really can be any string you want.

If the terraform workspace folders are in the root of the repo, you can leave the dir configuration option blank. Otherwise, you will need to set the dir to the path of the directory in which the terraform workspace folders are stored.

Configure your repo(s) to use the appropriate GitHub Actions (or similar workflow solution) to run the client. See the .github/example-workflows directory for example workflows. These are intentionally minimal, you will probably want to copy / paste and modify them to suit your needs.

Directories

Path Synopsis
cmd
internal
db
pkg

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL