This is a collection of examples demonstrating how to use the extended functionality implemented here with env = {} as a configuration parameter. The key objectives and reasons for this implementation are:
-
Each resource within one SOPS provider can have a different encrypted file with distinct encryption method(s) and separate environment-variable sets. In the example below, we use different AGE
recipientsfor different files. In practice, you could have any number of methods (e.g., AGE, GPG, etc.). -
We provide the
env = {}configuration parameter at resource level, not at provider level. This is important because setting it at provider level would mean a single command or secret/key for all SOPS files (or at least just a few different SOPS secrets engines where the ENV variables do not overlap). If you set it at provider level, it becomes very difficult to support dynamic configuration (currently only supported in OpenTofu but not in Terraform) and impossible to use with Terraform modules (see this and this). -
We implement a Go mutex here to prevent race conditions when the same
ENVname is used with different values. The same outcome could be achieved (see example ordered-resources) but thedepends_onmeta-argument does not support dynamic configuration. Another alternative is to useterraform apply -parallelism=n, but in that case you lose speed. -
When using ephemeral resources with the
externalorsopsproviders, it guarantees that secrets provided throughenv = {}will not be exposed in the unencrypted Terraform state file. -
If you don’t need any parameters in the
provider {}block you can omit it in the Terraform configuration (all examples below reflect this).
-
In this example, resources are ordered by Terraform’s internal mechanism
depends_on. This guarantees that different ENVs do not overlap with each other. However, this was an early development example prior to implementing the Go mutex; it is no longer necessary. -
This example demonstrates the most straightforward way to use the feature
env = {}in SOPS resources by providing an exec command directly toSOPS_AGE_KEY_CMD. This only works for SOPS secrets engines that provide*_CMDenvironment variables to obtain the secret key. -
This example shows a universal method to obtain a secret key via a Terraform
externalresource. This approach enables usage of SOPS secrets engines that cannot provide*_CMDenvironment variables. The only requirement is that the SOPS provider must accept an ENV for passing the secret key—here we useSOPS_AGE_KEYfor the AGE-based SOPS secret engine. -
This example has the same functionality as external-multi-exec except: some smart logic avoids extra exec to an external secrets store if the private key is the same for multiple files. In that case we issue only one request/exec to the external secrets store. We check AGE
recipientsin this scenario.
-
Tested with OpenTofu
v1.10.6(expected to work with Terraform as well) -
In all examples we use a custom script
keepassxc-kphto retrieve private secret keys from KeePassXC by browser-integration protocol (see more at KeePassXC browser integration docs and protocol specification). You can replace this with the CLI of your secrets manager (e.g., Bitwarden, 1Password, etc.). -
In all examples we employ the AGE-based SOPS secret engine, as a modern open-source solution supporting both
SOPS_AGE_KEYandSOPS_AGE_KEY_CMDenvironment variables out of the box in SOPS. -
Example files encrypted with the following AGE keys:
# Age Key #0 age1a64y5xkthnfzknteqhr44cfrsc89vwtqkwjm33dt00hdw5lmkdpq38vlmj AGE-SECRET-KEY-10667F4KSEVS6QDUK08HHWR64S6GVMM74MDYJ8AG99V32RGU5VXVQ86ZSJQ# Age Key #1 age1ju940d0eeanelukuvv77sjzf9nq0qzwfcmznm8r9sjvej3n6fqfqz2y6xp AGE-SECRET-KEY-1Q8SNRMDJQ3SN53L8M8QGYL3KH3R2ECKU0ERTLCML4N42UG3F447S58Z80WYou will need to add these to your secrets manager to test the examples here (or generate your own AGE keys and your own SOPS-encrypted files).
- Official SOPS documentation: https://getsops.io/
- Official AGE encryption tool documentation: https://github.com/age-sops/age
- Rage: Rust implementation of Age: https://github.com/str4d/rage
- Original Terraform provider for SOPS: https://registry.terraform.io/providers/carlpett/sops/latest/docs
- Fork of Terraform provider for SOPS: https://registry.terraform.io/providers/binlab/sops/latest/docs