Manage FusionAuth With Terraform
On this page
The FusionAuth Terraform provider manages FusionAuth configuration as Terraform resources. This guide creates a Tenant and an Application with Terraform. Then the guide brings the default Tenant and the FusionAuth Application under Terraform management. To decide what belongs in Terraform, see Manage FusionAuth Configuration Across Environments.
Before you begin, install or set up:
- A running FusionAuth instance. To start an instance locally, see Docker.
- Terraform 1.5 or later, or OpenTofu 1.6 or later.
The example repository contains the configuration from this guide.
Create Resources#
Get an API Key#
Terraform calls the FusionAuth API with an API key. Terraform can't create the first key.
Create the key at Settings -> API Keys . A key with no endpoints selected can call every API. To limit what Terraform can change, select only the endpoints for the resources that Terraform manages. See Managing API Keys.
On a new instance, Kickstart can create the key instead:
{
"apiKeys": [
{
"key": "4737ea8520bd454caabb7cb3d36e14bc1832c0d3f70a4189b82598670f11b1bd"
}
]
}Configure the Provider#
Create a directory for the configuration. Add a main.tf file that requires the provider:
terraform {
required_providers {
fusionauth = {
source = "fusionauth/fusionauth"
version = "~> 1.3"
}
}
}
# reads the API key and URL from FA_API_KEY and FA_DOMAIN
provider "fusionauth" {}The provider reads the instance URL and the API key from environment variables. Set both variables, so neither value appears in the configuration:
$ export FA_DOMAIN="https://auth.example.com" && export FA_API_KEY="<api-key>"
Download the provider:
$ terraform init
Define the Resources#
Add a Tenant and a signing key to main.tf:
resource "fusionauth_tenant" "forum" {
name = "Forum"
lifecycle {
prevent_destroy = true
}
}
resource "fusionauth_key" "forum" {
algorithm = "RS256"
length = 2048
name = "Forum access token key"
}The prevent_destroy setting stops Terraform from deleting the Tenant. Deleting a Tenant deletes every user, Application, and group in the Tenant.
Add an Application to the Tenant, with an admin role and a user role:
resource "fusionauth_application" "forum" {
tenant_id = fusionauth_tenant.forum.id
name = "Forum"
jwt_configuration {
enabled = true
access_token_id = fusionauth_key.forum.id
}
lifecycle {
# the forum app writes its own settings to this field through the API
ignore_changes = [data]
}
}
resource "fusionauth_application_role" "admin" {
application_id = fusionauth_application.forum.id
name = "admin"
is_super_role = true
}
resource "fusionauth_application_role" "user" {
application_id = fusionauth_application.forum.id
name = "user"
is_default = true
}The Application signs access tokens with the signing key. Handle Changes Made Outside Terraform explains the lifecycle block.
The provider documentation lists every resource and argument.
Apply the Configuration#
Preview the changes, then apply the changes:
$ terraform plan && terraform apply
Terraform lists the five resources to create and asks for confirmation. After the apply finishes, the Forum Application appears at Applications .
To change a resource later, edit main.tf. Then run terraform plan and terraform apply again. To delete a resource, remove the resource from main.tf and apply.
Manage the Default Resources#
The default Tenant and the FusionAuth Application exist before Terraform runs, so Terraform can't create these resources. To change the settings of a default resource, import the resource. To use a default resource without changing the resource, reference the resource with a data source.
Import the Default Resources#
- Find the Id of the default Tenant at Tenants . The FusionAuth Application has the Id
3c219e58-ed0e-4b18-ad48-f4f92793ae32on every instance.
- Add an import block and a resource for the default Tenant and for the FusionAuth Application. Give each resource only the required arguments. Replace
<default-tenant-id>with the Id of the default Tenant:Import the default resources import { to = fusionauth_tenant.default id = "<default-tenant-id>" } resource "fusionauth_tenant" "default" { name = "Default" lifecycle { prevent_destroy = true } } import { to = fusionauth_application.fusionauth id = "3c219e58-ed0e-4b18-ad48-f4f92793ae32" } resource "fusionauth_application" "fusionauth" { tenant_id = fusionauth_tenant.default.id name = "FusionAuth" lifecycle { prevent_destroy = true } } - Run
terraform plan. For each setting the configuration leaves out, the provider plans a change to a default value. The plan lists every value that differs from the instance:Output # fusionauth_tenant.default will be updated in-place # (imported from "d7d09513-a3f5-401c-9685-34ab6c552453") ~ resource "fusionauth_tenant" "default" { ... ~ email_configuration { ... - default_from_name = "FusionAuth" -> null ... - implicit_email_verification_allowed = true -> null ... Plan: 2 to import, 0 to add, 2 to change, 0 to destroy. - Copy each current value into the configuration. The current value is on the left of the arrow.
- Run
terraform planagain. Repeat the previous step until the plan shows0 to change. On a new instance, the configuration looks like this:Import the default resources with the current values import { to = fusionauth_tenant.default id = "<default-tenant-id>" } resource "fusionauth_tenant" "default" { name = "Default" email_configuration { default_from_name = "FusionAuth" implicit_email_verification_allowed = true } jwt_configuration { refresh_token_revocation_policy_on_login_prevented = true refresh_token_revocation_policy_on_password_change = true } phone_configuration { implicit_phone_verification_allowed = true } lifecycle { prevent_destroy = true } } import { to = fusionauth_application.fusionauth id = "3c219e58-ed0e-4b18-ad48-f4f92793ae32" } resource "fusionauth_application" "fusionauth" { tenant_id = fusionauth_tenant.default.id name = "FusionAuth" jwt_configuration { enabled = true refresh_token_expiration_policy = "SlidingWindow" refresh_token_ttl_minutes = 60 refresh_token_usage_policy = "OneTimeUse" ttl_seconds = 60 } lifecycle { prevent_destroy = true } } - Run
terraform applyto finish the import.
The values depend on the FusionAuth version and on any changes made in the Admin UI. Copy the values from the plan for your instance, not from the example.
FusionAuth doesn't allow deleting the default Tenant or the FusionAuth Application. With prevent_destroy set, Terraform stops at the plan. Without prevent_destroy, the apply fails partway through.
Keep the import blocks in the configuration. The blocks record that these resources came from FusionAuth, not from Terraform.
Reference a Resource Without Managing It#
A data source looks up an existing resource without managing the resource. This example creates an Application in the default Tenant without importing the Tenant:
data "fusionauth_tenant" "default" {
name = "Default"
}
resource "fusionauth_application" "forum" {
tenant_id = data.fusionauth_tenant.default.id
name = "Forum"
}The provider documentation lists the available data sources.
Handle Changes Made Outside Terraform#
A change made in the Admin UI or through the API doesn't update the Terraform configuration. The next terraform apply reverts the change. Run terraform plan first to see what the apply reverts.
To keep all changes in Terraform, limit who can change configuration in the Admin UI. See Admin UI roles.
Some changes are expected. For example, an app might store settings in the data field of the FusionAuth Application that represents the app. List fields like data in ignore_changes, as the Forum Application does:
lifecycle {
ignore_changes = [data]
}
To create a resource with Terraform but never update the resource, use ignore_changes = all.
Protect Secrets#
Terraform state contains secrets, such as OAuth client secrets and API keys. Before sharing state with a team, store the state in an encrypted backend with restricted access. See Secrets.
To create more API keys with Terraform, use the fusionauth_api_key resource. With Terraform 1.11 or later, set the key with the write-only key_wo argument. Terraform doesn't store write-only values in state.
Limitations#
Terraform can't create the first API key. Create the first key in the Admin UI or with Kickstart.
The provider supports most of the FusionAuth API. Support for a new feature can arrive a few weeks after the FusionAuth release. To request a missing resource or argument, open an issue in the provider repository. Until then, manage that part of the configuration with a script.