
OVHcloud users rejoice: OpenTofu state encryption now works with OVHcloud KMS, so you can protect your state and plan files with managed keys.
When people think about Infrastructure as Code, Terraform is often the first tool that comes to mind. However, since the fork that led to OpenTofu, the project has evolved significantly and introduced features that help it stand out.
One of the most notable additions is state and plan encryption, a capability the community had been requesting for years.
OpenTofu now supports encryption of state and plan files at rest, whether they are stored locally or in a remote backend. This helps teams protect sensitive infrastructure metadata, credentials, and configuration details that may otherwise be exposed through state files. To manage encryption keys, OpenTofu integrates with several Key Management Services (KMS), including OpenBao, AWS KMS, Google Cloud KMS, and Azure Key Vault.
OVHcloud users now have that option too, thanks to the OpenTofu external key provider for OVHcloud KMS, which OVHcloud maintains. It lets them use managed encryption keys to secure their infrastructure state.
In this blog post, I’ll show you how to use OVHcloud KMS to encrypt your OpenTofu state and plan files, improving the security of your Infrastructure as Code workflows.
What does encryption protect against?
When enabled, OpenTofu encrypts state data at rest, helping protect sensitive information in the state file from unauthorized access.
However, encryption is not a substitute for backup, versioning, or state management mechanisms. It does not prevent data loss, and by itself cannot protect against all forms of state corruption or replay attacks involving older state or plan files. It also cannot prevent users running the tofu command from accessing sensitive values, as they must be able to read the decrypted state.
⚠️ Please keep in mind that when you enable encryption, your state and plan files become unrecoverable without the appropriate encryption key.
Prerequisites
Before getting started, be sure you meet the following prerequisites:
- An OVHcloud account
- An OKMS (OVHcloud Key Management Service) domain (“305db938-1234-5678-9012-3a0a29291661” for example in this blog post)
- A Service Key of type “AES” with the “Wrap/Unwrap” usage (“16491ef8-aaaa-bbbb-cccc-50ac90ab8e56” for example in this blog post)
- An IAM (Identity and Access Management) local user (“kms-305db938-1234-5678-9012-3a0a29291661” for example in this blog post)
- The OVHcloud CLI, installed
- The OpenTofu CLI, installed (v1.13+, where external key providers are no longer experimental)
💡 The OVHcloud KMS key provider supports both token and mutual TLS (mTLS) authentication. In this blog post, we will use the token authentication mode. Please follow the official key provider guide if you wish to use mTLS authentication mode.
Set up the key provider
Install the key provider
Install the key provider locally:
curl -fsSL https://raw.githubusercontent.com/ovh/opentofu-kms-ovhcloud/main/install.sh | sh⚠️ The binary is installed in $HOME/.local/bin by default (created if it does not exist). Make sure this directory is in your PATH.
Or follow the other installation methods.
Now you can use the OVHcloud KMS key provider 🎉.
Find your OKMS ID
List the OKMS domains:
$ ovhcloud okms list
┌──────────────────────────────────────┬─────────────┐
│ id │ region │
├────────────────────────────── ────────┼─────────────┤
│ 305db938-1234-5678-9012-3a0a29291661 │ eu-west-par │
│ xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx │ eu-west-par │
└──────────────────────────────────────┴─────────────┘Save the OKMS ID in an environment variable:
export KMS_RESTAPI_OKMSID="305db938-1234-5678-9012-3a0a29291661"Generate a token (token authentication only)
The OpenTofu encryption feature requires permission to access and use keys from OVHcloud KMS.
If you want to use token authentication, you’ll need a personal access token (PAT). You can use the OVHcloud CLI to do that:
KMS_RESTAPI_TOKEN=$(ovhcloud iam user token create <iam-local-user-name> --name pat-opentofu-<iam-local-user-name> --description "PAT opentofu for domain $KMS_RESTAPI_OKMSID" -o json | jq .details.token | tr -d '"')
echo $KMS_RESTAPI_TOKENYou should have a result like this:
$ KMS_RESTAPI_TOKEN=$(ovhcloud iam user token create kms-305db938-1234-5678-9012-3a0a29291661 --name pat-opentofu-kms-305db938-1234-5678-9012-3a0a29291661 --description "PAT opentofu for domain 305db938-1234-5678-9012-3a0a29291661" -o json | jq .details.token | tr -d '"')
2026/05/07 08:48:34 Final parameters:
{
"description": "PAT opentofu for domain 305db938-1234-5678-9012-3a0a29291661",
"name": "pat-opentofu-kms-305db938-1234-5678-9012-3a0a29291661"
}
$ echo $KMS_RESTAPI_TOKEN
eyJhbGciOiJFZE...ASgXy55_DDFHdy4Z5uSq8lww-BwSave the KMS information
Store the settings in environment variables. For example:
export KMS_RESTAPI_TYPE=token
export KMS_KEY_ID="<your_service_key_id>"
export KMS_RESTAPI_ENDPOINT=$(ovhcloud okms get $KMS_RESTAPI_OKMSID -o json | jq .restEndpoint | xargs)Display the saved information:
$ echo $KMS_RESTAPI_TYPE
token
$ echo $KMS_RESTAPI_ENDPOINT
https://eu-west-par.okms.ovh.net
$ echo $KMS_KEY_ID
16491ef8-aaaa-bbbb-cccc-50ac90ab8e56
$ echo $KMS_RESTAPI_TOKEN
eyJ...BIoHCAEnable OpenTofu state encryption
Create a provider.tf file with the following content:
terraform {
encryption {
key_provider "external" "ovhcloud" {
command = [
"opentofu-kms-ovhcloud",
# Key ID, of the service key used to wrap/unwrap the data key, will be automatically filled with the $KMS_KEY_ID environment variable
# RestAPI Token will be automatically filled with the $KMS_RESTAPI_TOKEN environment variable
# OKMS ID will be automatically filled with $KMS_RESTAPI_OKMSID environment variable
]
}
method "aes_gcm" "ovhcloud" {
keys = key_provider.external.ovhcloud
}
state {
method = method.aes_gcm.ovhcloud
}
plan {
method = method.aes_gcm.ovhcloud
}
}
}
In this example we will create a my-null-resource.tf file with the following content:
resource "null_resource" "test" {}💡 Note: in a real project, use your “*.tf” existing files instead of this example resource.
Initialize OpenTofu project:
$ tofu init
Initializing the backend...
Initializing provider plugins...
- Finding latest version of hashicorp/null...
- Installing hashicorp/null v3.3.2...
- Installed hashicorp/null v3.3.2 (signed, key ID 0C0AF313E5FD9F80)
Providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://opentofu.org/docs/cli/plugins/signing/
OpenTofu has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that OpenTofu can guarantee to make the same selections by default when
you run "tofu init" in the future.
OpenTofu has been successfully initialized!
You may now begin working with OpenTofu. Try running "tofu plan" to see
any changes that are required for your infrastructure. All OpenTofu commands
should now work.
If you ever set or change modules or backend configuration for OpenTofu,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.Apply it:
$ tofu apply
OpenTofu used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
OpenTofu will perform the following actions:
# null_resource.test will be created
+ resource "null_resource" "test" {
+ id = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
OpenTofu will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
null_resource.test: Creating...
null_resource.test: Creation complete after 0s [id=5663203269009606061]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Display the state file:
$ cat terraform.tfstate | jq
{
"serial": 1,
"lineage": "ba70b37d-aaaa-bbbb-cccc-25cd80b64d26",
"meta": {
"key_provider.external.ovhcloud": "eyJleHRlcm5………idyJ9fQ=="
},
"encrypted_data": "EXCFfXjgFmevAs..b……………………………fHz8mXkE/Oh3qMip7l3D7TX5vt",
"encryption_version": "v0"
}The state is now encrypted 🎉.
💡 Note that each time you execute the tofu plan or tofu apply commands, a new ephemeral data key is generated and protected using your OVHcloud Service Key.
Conclusion
This post showed how to set up OpenTofu state encryption with OVHcloud KMS, so that both your state and plan files are protected.
With the OVHcloud KMS key provider, you can bring managed encryption keys directly into your Infrastructure as Code (IaC) workflows.
Our team is working on several other integrations. In the meantime, try the key provider and as always, we welcome you to share your feedback on the GitHub repository!