OpenTofu Configuration
The opentofu
project extension allows for the configuration of toolchains and source sets.
Toolchains
OpenTofu toolchains are configured in the toolchains
block.
On xref:platform-installation-support.adoc it offers the option of configuring a OpenTofu version, which Gradle will then download, cache and use.
By default, you do not need to configure anything as there will be a toolchain called standard
which will have a version of OpenTofu defined.
You can override this version.
opentofu {
toolchains {
standard {
executableByVersion('1.8.8') (1)
executableByPath('/here/is/my/installed/tofu') (2)
executableBySearchPath('tofu') (3)
}
}
}
1 | Use a specific version. If it is not available locally, then Gradle will bootstrap and cache it. |
2 | Use a fixed location of the executable. |
3 | Look in the system search path for an executable called tofu (or tofu.exe on Windows). |
Backends
Backends is where remote state is stored. A configuration needs to have at least one backend defined which are then referenced by name from the source sets.
import org.ysb33r.gradle.opentofu.backends.S3Backend (1) opentofu { backends { s3(S3Backend) { (2) bucket = 'my-bucket' (3) } } sourceSets { main { useBackend('s3') (4) } } }
1 | Import the backend that you need. |
2 | Name the backend and use the appropriate type that was imported.
In this case the backend is named s3 |
3 | Configure the backend appropriately depending on the backend type. |
4 | Link a source set to a specific backend.
The following backend types are supported out of the box:
|
Source Sets
Source sets are managed from opentofu.sourceSets
.
When the org.ysb33r.opentofu
plugin is applied, it adds a main
source set.
Associated with this source set is the default folder of src/tf/main
and a number of tasks including:
-
tofuApply
-
tofuCleanupWorkspaces
-
tofuDestroy
-
tofuDestroyPlan
-
tofuFmtCheck
-
tofuFmtApply
-
tofuImport
-
tofuInit
-
tofuOutput
-
tofuPlan
-
tofuShowState
-
tofuStateMv
(tofu state mv
) -
tofuStatePush
(tofu state push
) -
tofuStateRm
(tofu state rm
) -
tofuValidate
If additional source sets are needed, they can be added by convention i.e.
opentofu {
sourceSets {
s3Buckets (1)
cloudFront {
srcDir = 'src/elsewhere/cf' (2)
}
}
}
1 | Creates an OpenTofu source set named 's3Buckets' with default directory src/tf/s3Buckets |
2 | Creates an OpenTofu source set named 'cloudFront` and set the directory to src/elsewhere/cf . |
Tasks for additional source sets follow the tofo<SourceSetName><OpenTofuCommand>
format.
For instance in the above example the initialisation task for the s3Buckets
source set will be called tofuS3BucketsInit
.
By convention all tasks that map OpenTofu commands start with tofu
.
Other non-commands tasks might start with opentofu
or contain OpenTofu
within the task name.
Configuring source sets
opentofu {
sourceSets {
main {
srcDir = 'src/tf/main' (1)
variables { (2)
var 'aws_region', 'us-east-1'
}
secondarySources 'src/tf/modules' (3)
workspaces 'alpha', 'beta' (4)
useBackend 's3' (5)
}
}
}
1 | Source directory, which will also be the working directory for terraform. |
2 | Configure any variables that are specific to the source set. See the Variables block for more details. |
3 | Additional sources that should affect re-running of tasks, but which are not directly part of the existing source set. |
4 | Adds additional workspaces to the source set. |
5 | Link this source set to a named backend. |
Variables
A number of OpenTofu task types support variables and files containing variables.
Any variables
block support the following functionality
variables {
var 'foo', 'bar' (1)
map 'fooMap', foo: 'bar' (2)
list 'fooList', 'foo', 'bar' (3)
list 'fooList', [ 'foo', 'bar'] (4)
files 'filename1.tfvars' , 'filename2.tfvars'(5)
provider { VariablesSpec v -> v.var('foo2','bar2')} (6)
remoteStateMap {
injectVar = true (7)
varName = 'my_alt_remote_state' (8)
}
}
1 | Adds one variable called foo or value bar .
The provided value can be anything that can be lazy-evaluated to a string. |
2 | Adds a map called fooMap .
The map keys have to be strings, but the map values can be anything that will lazy-evaluate to a string. |
3 | Adds a list called fooList with values foo and bar .
The list entries can be anything that will lazy-evaluate to a string. |
4 | Alternative list format |
5 | Adds one or more files containing a list of terraform variables. The names can be anything that will lazy evaluate to a string and may be a relative path. The files will be resolved relative to the source set directory. |
6 | Adds an action that will be called on a VariablesSpec instance to provide more variables.
These actions will be evaluated before any of the above methods are evaluated. |
7 | Inject the tokens from the backend into a map called remote_state . |
8 | Use an alternative variable name than remote_state . |
Workspaces
This plugin suite adds naming conventions to easily deal with workspaces.
If you add a workspace called alpha
then the apply task for this workspace will be tofuApplyAlpha
.
If you add a workspace called beta
to a source set called release
, then the apply task will be tofuReleaseApplyBeta
.
These conventions only apply to tasks which are workstate/state-aware in terraform.
For instance there will be no task named tofuInitAlpha
or tofuFmtCheckAlpha
.
There is also no need to switch workspaces, as the plugin will do that under the hood automatically.
If you run ./gradlew tofuApplyAlpha tofuApplyBeta tofuApplyGamma tofuOutput
, the plugin will automatically perform a a tofu select
before executing tofu apply
or tofu output
.
If you decide to remove workspaces, simply cleanup the state by running the appropriate tofuDestroy
task(s) and then remove the workspaces from the source set DSL. Finally run tofuCleanupWorkspaces
.
Override OpenTofu version for a source set
Sometimes you might need one of your source sets to run with a different version of OpenTofu. For instance, you might want to upgrade, but one of the source sets will take more work and leaving it at an older version for a period might be a good solution.
Assuming that you have sourceSet called monkey
, this can be achieved via task actions
opentofu {
toolchains {
legacyTofu { (1)
executableByVersion('1.8.3')
}
}
sourceSets {
monkey {
useToolchain('legacyTofu') (2)
}
}
}
1 | Create a toolchain for the legacy version |
2 | Tell your source set to use that toolchain. |
Configure multiple source sets
opentofu {
sourceSets.all { (1)
useBackend('s3')
}
}
1 | Apply this configuration to all source sets. |
Have relationships between source sets
Sometimes you need to ensure that the infrastructure of one source set is applied, before applying that of another source set. There is a simple DSL method to eliminate the need for you to add all the inter-tasks dependencies.
opentofu {
sourceSets {
database {
}
etl {
mustRunAfter('database') (1)
}
}
}
1 | Ensures that tfEtlApply and tfEtlPlan will run after tfDatabaseApply* and tfDatabasePlan* .
The relationship is a simple mustRunAfter . |
Configure the plugin cache directory
Be default the plugin cache directory from terraformrc
in the root project is used.
It is possible to also use a custom plugin cache directory per source set.
terraform {
sourceSets {
main {
useConfiguredPluginCache() (1)
useCustomPluginCache() (2)
useCustomPluginCache('path/to/my/cache', 100000) (3)
}
}
}
1 | Use the plugin cache directory that is configured in terraformrc (Default behaviour). |
2 | Use a custom plugin cache directory for this source set. This cache dir is stored under the project cache directory |
Secrets
You can create arbitrary secrets and share them with backends and source sets
import org.ysb33r.gradle.iac.base.secrets.AwsSecrets
opentofu {
secrets {
awsAcct1(AwsSecrets) { (1)
useAccessKeyId('1234567890')
useSecretAccessKey('abcdefghijklmn')
}
}
backends {
s3(S3Backend) {
fromSecretsProvider(opentofu.secrets.awsAcct1) (2)
}
}
sourceSets {
main {
fromSecretsProvider(opentofu.secrets.awsAcct1) (3)
}
}
}
1 | Declare a collection of secrets.
In this example we use org.ysb33r.gradle.iac.base.secrets.AwsSecrets for declaring AWS secrets like profile, credentials file, access keys etc. |
2 | Tell the S3 backend to use the same secrets that was defined in our secrets definition. |
3 | You can also tell the source set to use the values from a secrets definition. |
You can also use org.ysb33r.gradle.iac.base.secrets.GenericSecrets
as a way of rolling your own secrets.