Panfactum LogoPanfactum
Infrastructure-as-CodeDeploying Modules

Deploying Modules

Prerequisites

This guide assumes:

  • You have prior experience working with infrastructure tools such as OpenTofu (Terraform) and Terragrunt. If you are not, please review the concept docs before proceeding.

  • You have completed the necessary repository setup guide.

Using Terragrunt to Deploy Modules

In the Panfactum stack, we use Terragrunt to configure and deploy OpenTofu / Terraform modules.

The configuration-as-code for each deployed module is stored in your repository's environments folder. 1 Terragrunt uses an extended version of the Hashicorp Configuration Language (HCL) to define each module's deployment configuration. If you are new to terragrunt, you should review the basics of the syntax here.

Terragrunt wraps tofu / terraform CLI and contains the same subcommands (e.g., apply, plan, etc.). Simply navigate to the module subdirectory in your environments folder and run the desired command (e.g., terragrunt apply).

Terragrunt also provides a run-all subcommand that allows you to deploy every module inside a directory which can be very helpful for updating all the modules in a particular environment (e.g., terragrunt run-all apply).

Local Deployments

When working with modules locally, it can be helpful to override your organization's values in the standard terragrunt configuration files. For example, you may want to use a different provider configuration to pull in your specific AWS profile, or you may want to test a specific module version.

Each of the standard files has .user.yaml counterparts that will override the normal file values:

  • global.yaml: global.user.yaml
  • environment.yaml: environment.user.yaml
  • region.yaml: region.user.yaml
  • module.yaml: module.user.yaml

These files are automatically ignored by version control.

CI Systems

We are actively working on providing CI templates to automate the deployment process of the infrastructure. This should be available in the next release.

Defining a Module for Deployment

When defining infrastructure modules for deployment, each module will receive its own folder. You will add a terragrunt.hcl file to each folder which will contain your deployment configuration.

To take advantage of the Panfactum stack, your terragrunt.hcl must at minimum have the following include block:

include "panfactum" {
  path = find_in_parent_folders("panfactum.hcl")
  expose = true
}

You will also likely want to include a terraform source such as the following:

terraform {
  source = "github.com/Panfactum/stack.git//packages/infrastructure/aws_eks"
}

The source syntax follows these rules..

We do provide some convenience utilities for setting easily setting the source if you are:

  • using a Panfacutm module

  • using a first-party module (defined in your infrastructure repo)

Finally, you will likely want an inputs field to define the provided inputs to the sourced infrastructure module:

inputs = {
  foo = "bar"
}

A full file example might look something like this:

include "panfactum" {
  path = find_in_parent_folders("panfactum.hcl")
  expose = true
}

terraform {
   source = "github.com/Panfactum/stack.git//packages/terraform/aws_eks"
}

inputs = {
  foo = "bar"
}

Setting up Providers

Before you deploy a module, you must setup your OpenTofu (Terraform) providers.

We provide ready-made configuration for many providers (see the full list here).

To enable a provider, add the provider under providers key of the module's module.yaml.

For example, this module.yaml would enable the aws and kubernetes provider for a module:

providers:
  - aws
  - kubernetes

Some providers require extra configuration variables. To view each provider's required configuration variables, see the reference docs.

Generally, you will want to configure a provider on an environment-wide or region-wide basis (i.e., in the environment.yaml or region.yaml files).

Determining Which Providers to Enable

There are two different methods to determine which providers to enable.

Direct Method

Every OpenTofu module will have a required_providers block that looks like this:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.10"
    }
    tls = {
      source  = "hashicorp/tls"
      version = "4.0.4"
    }
  }
}

You can use this information to determine which providers to enable. In the above case, it would be the aws and tls providers.

Indirect Method

As modules can import other modules (which may have their own discrete required providers), the direct method may be tedious depending on the depth of the module tree.

You can instead rely on the .terraform.lock.hcl file that gets generated after running terragrunt init in the module directory. You can run terragrunt init without first configuring modules.

The .terraform.lock.hcl file may look as follows:

# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.

provider "registry.terraform.io/hashicorp/aws" {
  version     = "5.10.0"
  constraints = "5.10.0"
  hashes = [
    "h1:AgF54/79Nb/oQjbAMMewENSIa1PEScMn20Xa91hZR2g=",
    "h1:csg7yqBE71epsXpVFPn4p8sCKoBNCFLfJq3Qa61XNQ4=",
    "zh:24f8b40ba25521ec809906623ce1387542f3da848952167bc960663583a7b2c7",
    "zh:3c12afbda4e8ed44ab8315d16bbba4329ef3f18ffe3c0d5ea456dd05472fa610",
    "zh:4da2de97535c7fb51ede8ef9b6bd45c790005aec36daac4317a6175d2ff632fd",
    "zh:5631fd3c02c5abe5e51a73bd77ddeaaf97b2d508845ea03bc1e5955b52d94706",
    "zh:5bdef27b4e5b2dcd0661125fcc1e70826d545903b1e19bb8d28d2a0c812468d5",
    "zh:7b7f6b3e00ad4b7bfaa9872388f7b8014d8c9a1fe5c3f9f57865535865727633",
    "zh:935f7a599a3f55f69052b096491262d59787625ce5d52f729080328e5088e823",
    "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
    "zh:a451a24f6675f8ad643a9b218cdb54c2af75a53d6a712daff46f64b81ec61032",
    "zh:a5bcf820baefdc9f455222878f276a7f406a1092ac7b4c0cdbd6e588bff84847",
    "zh:c9ab7b838a75bbcacc298658c1a04d1f0ee5935a928d821afcbe08c98cca7c5f",
    "zh:d83855b6d66aaa03b1e66e03b7d0a4d1c9f992fce06f00011edde2a6ad6d91d6",
    "zh:f1793e9a1e3ced98ca301ef1a294f46c06f77f6eb10f4d67ffef87ea60835421",
    "zh:f366c99ddb16d75e07a687a60c015e8e2e0cdb593dea902385629571bd604859",
    "zh:fb3ec60ea72144f480f495634c6d3e7a7638d7061a77c228a30768c1ae0b91f6",
  ]
}

Using Secrets

Example

Secrets are encrypted and stored directly inside the repository just like any other configuration setting. The Panfactum stack uses sops for ensuring this is done securely and in a manner that integrates with terragrunt. See our guide for setting up sops.

Assuming you have an encrypted file called secrets.yaml adjacent to your terragrunt.hcl, you can access its values using the following syntax: 2

locals {
  secrets = yamldecode(sops_decrypt_file("${get_terragrunt_dir()}/secrets.yaml"))
}

inputs = {
  secret_input = local.secrets.my_secret
}

Defining the Dependency Graph

Example

Typically, there will be an order in which your infrastructure modules must be deployed and updated. For example, you will need to launch your Kubernetes cluster before you can deploy Helm charts to it.

You should explicitly declare these dependencies via terragrunt dependency blocks (docs).

This will ensure that modules are always updated in the desired order. Additionally, you can use this functionality to pass the outputs of one module into the inputs of another.

Resource Tagging

All Panfactum modules deploy resources with standard tags / labels..

You can add arbitrary additional tags by setting extra_tags in any of the following files:

  • global.yaml
  • environment.yaml
  • region.yaml
  • module.yaml

extra_tags is an object where the keys and values must both be strings.

The defined tags will be applied to all modules in the specified scope. For example, if extra_tags is defined in environment.yaml, all resources in that environment will receive the tags.

Footnotes

  1. This folder lives in your stack repository, is defined by the environment variable PF_ENVIRONMENTS_DIR, and was created when running pf-update-terragrunt. For more information, see our repository setup guide

  2. Note that you must use the get_terragrunt_dir() function to resolve the filepath. This will ensure a stable location regardless of how terragrunt is run.