Terraform
Manage projects, environments, SDK keys, flags, targeting rules, segments, release templates, triggers and webhooks as code.
Set up the provider
terraform {
required_providers {
featflags = { source = "featflags/featflags" }
}
}
provider "featflags" {
api_url = "https://app.featflags.io" # or FEATFLAGS_API_URL
change_comment = "Applied by Terraform" # recorded with every change
}
The provider signs in with an API token (fft_…) in FEATFLAGS_TOKEN. Create one under Settings → API tokens, ideally a service token. A token belongs to one organisation, and Terraform manages that organisation’s projects, so there’s no organisation to configure; the token’s roles decide what Terraform can change.
change_comment goes into history with every change Terraform makes. Environments that require a comment refuse changes without one.
Resources
| Resource | What | Import ID |
|---|---|---|
featflags_project |
A project. | project |
featflags_environment |
An environment, with its allowed web origins and whether it requires comments. Exports its client_key and server_key. |
project/environment |
featflags_sdk_key |
Another client or server key for an environment, for rotation, with an optional expiry. Exports the key when it’s created; destroying it revokes it. |
project/environment/key_id |
featflags_flag |
A flag: its variants, client visibility, and whether it’s temporary or archived. Destroying it archives it, then deletes it. | project/flag |
featflags_flag_state |
A flag in one environment: on or off, prerequisites, individual targets, default and off variant. | project/flag/environment |
featflags_flag_rule |
One targeting rule, placed by priority. |
project/flag/environment/rule |
featflags_segment |
A reusable audience. | project/segment |
featflags_release_template |
A release template. | project/template |
featflags_flag_trigger |
A trigger URL. Exports the url, sensitive, when it’s created. |
project/flag/environment/trigger_id |
featflags_webhook |
A webhook or Slack notification. Exports its signing secret when it’s created. |
project/webhook_id |
The data source featflags_environment reads an existing environment’s keys.
An example
resource "featflags_project" "acme" {
key = "acme"
name = "Acme"
}
resource "featflags_environment" "production" {
project = featflags_project.acme.key
key = "production"
allowed_origins = ["https://app.acme.com"]
require_comment = true
}
resource "featflags_segment" "staff" {
project = featflags_project.acme.key
key = "staff"
match = [{ conditions = [{ op = "ends_with", attr = "email", values = ["@acme.com"] }] }]
}
resource "featflags_flag" "new_checkout" {
project = featflags_project.acme.key
key = "new-checkout"
description = "Checkout rewrite"
client_visible = true # a boolean flag: variants on (true) and off (false)
}
resource "featflags_flag_state" "new_checkout_production" {
project = featflags_project.acme.key
flag = featflags_flag.new_checkout.key
environment = featflags_environment.production.key
enabled = true
default = {
rollout = {
split = [
{ variant = "on", percent = 5 },
{ variant = "off" }, # no percent: gets the rest
]
}
}
}
resource "featflags_flag_rule" "new_checkout_staff" {
project = featflags_project.acme.key
flag = featflags_flag.new_checkout.key
environment = featflags_environment.production.key
rule_id = "staff"
priority = 10
conditions = [{ op = "in_segment", segments = [featflags_segment.staff.key] }]
serve = { variant = "on" }
}
In a rollout’s split, leave percent out of one entry and it gets whatever the others leave.
Working alongside the app
- Rules are separate resources, so rules people add in the app live next to the ones Terraform manages, ordered by priority.
- To let people flip a flag in the app without Terraform turning it back, ignore
enabledin the flag state’slifecycle { ignore_changes = [enabled] }. - Keep long key lists out of Terraform. Individual targets and
in_securevalues are plain text in Terraform’s state. Upload big lists withfeatflags lists upload; the provider leaves uploaded lists alone. - Approvals. In an environment that requires approval, a change Terraform makes is held for review instead of applied: the apply fails naming the held change and records nothing in state, so the next plan shows the change again until it’s approved in featflags.
- Rate limits. When featflags asks it to slow down, the provider waits and retries, up to five attempts.