Panfactum LogoPanfactum
Infrastructure-as-CodeExtending Panfactum Configuration

Extending the Panfactum Configuration

While our panfactum.hcl provides a lot of functionality out-of-the-box, you may want to add or change functionality on a global basis.

To accomplish this, define your own shared.hcl with its own behavior in the root of the environment folder.

Add the following include block to each module's terragrunt.hcl after the "panfactum" include:

include "shared" {
  path = find_in_parent_folders("shared.hcl")
}

This will merge your custom settings with the Panfactum default settings (see the docs for different merge strategies).

Accessing the Scoped YAML Variables

In your shared.hcl, you may find it helpful to reference values defined in the global.yaml, environment.yaml, region.yaml, and module.yaml files, especially because you can add arbitrary values to these files.

Unfortunately, it is not currently possible to import the resolved values directly from the panfactum.hcl into your shared.hcl due to this outstanding issue.

Until that issue is resolved, you should copy this block into your shared.hcl:

locals {
  global_raw_vars     = try(yamldecode(file(find_in_parent_folders("global.yaml"))), {})
  global_user_vars    = try(yamldecode(file(find_in_parent_folders("global.user.yaml"))), {})
  global_vars         = merge({}, local.global_raw_vars, local.global_user_vars)
  global_extra_tags   = lookup(local.global_vars, "extra_tags", {})
  global_extra_inputs = lookup(local.global_vars, "extra_inputs", {})

  environment_raw_vars     = try(yamldecode(file(find_in_parent_folders("environment.yaml"))), {})
  environment_user_vars    = try(yamldecode(file(find_in_parent_folders("environment.user.yaml"))), {})
  environment_vars         = merge({}, local.environment_raw_vars, local.environment_user_vars)
  environment_extra_tags   = lookup(local.environment_vars, "extra_tags", {})
  environment_extra_inputs = lookup(local.environment_vars, "extra_inputs", {})

  region_raw_vars     = try(yamldecode(file(find_in_parent_folders("region.yaml"))), {})
  region_user_vars    = try(yamldecode(file(find_in_parent_folders("region.user.yaml"))), {})
  region_vars         = merge({}, local.region_raw_vars, local.region_user_vars)
  region_extra_tags   = lookup(local.region_vars, "extra_tags", {})
  region_extra_inputs = lookup(local.region_vars, "extra_inputs", {})

  module_raw_vars     = try(yamldecode(file(find_in_parent_folders("${get_terragrunt_dir()}/module.yaml"))), {})
  module_user_vars    = try(yamldecode(file(find_in_parent_folders("${get_terragrunt_dir()}/module.user.yaml"))), {})
  module_vars         = merge({}, local.module_raw_vars, local.module_user_vars)
  module_extra_tags   = lookup(local.module_vars, "extra_tags", {})
  module_extra_inputs = lookup(local.module_vars, "extra_inputs", {})

  # Merge all of the vars with order of precedence
  vars = merge(
    local.global_vars,
    local.environment_vars,
    local.region_vars,
    local.module_vars
  )
  extra_tags = merge(
    local.global_extra_tags,
    local.environment_extra_tags,
    local.region_extra_tags,
    local.module_extra_tags
  )
  extra_inputs = merge(
    local.global_extra_inputs,
    local.environment_extra_inputs,
    local.region_extra_inputs,
    local.module_extra_inputs
  )
}

You will then be able to access the properly scoped variable via local.vars at other places in your shared.hcl configuration.

Supporting Additional Providers

One common customization will be adding additional OpenTofu (Terraform) providers for your organization's custom modules. We recommend aligning your provider configuration setup with how Panfactum defines and enables our built-in providers. This conforms to the official terragrunt recommendation for managing providers.

Let's assume that you want to build modules that require the Datadog terraform provider, and you want all modules to use the same provider settings.

  1. Ensure that each module has an include block for your custom shared.hcl (see above).

  2. Create a new datadog.tf file with the following contents (example). Add it to the environments/providers/ directory alongside the other provider snippets.

    provider "datadog" {
      api_url = "https://api.datadoghq.com/"
    }
    
  3. Add the following snippet to your shared.hcl:

    generate "datadog_provider" {
        path      = "datadog.tf"
        if_exists = "overwrite_terragrunt"
        contents  = strcontains(try(file(find_in_parent_folders("${get_terragrunt_dir()}/.terraform.lock.hcl")), ""), "registry.opentofu.org/DataDog/datadog") ? file("${path_relative_from_include()}/providers/datadog.tf") : ""
    }