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
).
While Terragrunt should have been properly configured when completing the bootstrapping guide, you can review the standard Panfactum configuration rules here.
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.
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.
All the standard Terragrunt syntax is available to you when using the Panfactum stack.
However, to take advantage of the Panfactum functionality, 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 want to include a terraform
source such as the following:
# Even though this is "terraform", it can source either Terraform or OpenTofu modules
terraform {
source = "github.com/owner/repo.git//some-module"
}
Finally, you will want an inputs
field to define the provided inputs to the sourced infrastructure module:
inputs = {
foo = "bar"
}
A simple, but complete example might look something like this:
include "panfactum" {
path = find_in_parent_folders("panfactum.hcl")
expose = true
}
terraform {
source = "github.com/owner/repo.git//some-module"
}
inputs = {
foo = "bar"
}
Versioning
The Terragrunt configuration values on the primary integration branch
(set by the repo_primary_branch
repo variable) are the values
that will be deployed by CI / CD pipelines. As a result, the source-of-truth for what is running every environment is consolidated in this
branch's environments folder.
However, this is only the configuration-as-code. The
infrastructure modules (i.e., infrastructure-as-code) may be pinned to specific versions via the terraform.source
field.
We provide two terragrunt variables for setting the module version:
-
pf_stack_version
: the Panfactum module version to use; used byinclude.panfactum.locals.pf_stack_source
. (See below) -
version
: the first-party module version to use; used byinclude.panfactum.locals.source
.
Deploying Panfactum Modules
If you are deploying a Panfactum module, use
include.panfactum.locals.pf_stack_source
to dynamically construct the terraform.source
. This adds version management,
caching, and other convenience functionality. For example:
terraform {
source = include.panfactum.locals.pf_stack_source
}
We use the following terragrunt variables to construct include.panfactum.locals.pf_stack_source
:
-
pf_stack_version
: Check here for the full list of available releases. This should be the same version as used for the Panfactum devShell. We recommend setting this in yourenvironment.yaml
so that functionality remains consistent across an entire environment. -
module
: The name of the module to deploy. Defaults to the directory name if not provided. Should be set in themodule.yaml
.
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).
We also provide a convenience command, pf-tf-init
, that will automatically inspect the sourced
infrastructure module and add the correct providers and provider hashes to your .terraform.lock.hcl
files. 2
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).
Using Secrets
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: 3
locals {
secrets = yamldecode(sops_decrypt_file("${get_terragrunt_dir()}/secrets.yaml"))
}
inputs = {
secret_input = local.secrets.my_secret
}
Defining the Dependency Graph
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.
Common Inputs
Many Panfactum modules share the same input variables so that particular behaviors can be enabled / disabled across many modules at once.
Shared inputs to groups of modules can be supplied by setting extra_inputs
in any of the following files:
global.yaml
environment.yaml
region.yaml
module.yaml
For a list of all the common input variables see this reference list.
Using a Local Copy of Panfactum Modules
You may want to make modifications to Panfactum modules found in the Stack repository and deploy them to your infrastructure.
You can easily do this for any set of modules by manipulating a few Terragrunt variables:
-
Clone the Panfactum Stack repository to your local machine (
git clone https://github.com/Panfactum/stack.git
). -
Set
pf_stack_version
tolocal
. -
Set
pf_stack_local_path
to an absolute path to the local copy of the Panfactum Stack repository you cloned in step 1.
Now when you next run terragrunt apply
, the local module code will be used. This applies to both Panfactum direct
modules and submodules. 4
Footnotes
-
This folder lives in your stack repository, is defined by the repo variable
environments_dir
, and was created when runningpf-update-terragrunt
. For more information, see our repository setup guide ↩ -
This uses
terragrunt providers
under the hood.terragrunt providers
can be used to provide more granular information about the required providers such as their versions. ↩ -
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. ↩ -
For submodules, this works via the
pf_module_source
input provided to your modules by Terragrunt. See the first-party IaC development documentation for more information. ↩