Feature Flags as Code: Managing Featureflip with Terraform

Manage feature flags as code with the Featureflip Terraform provider: declare projects, flags, and targeting in .tf files, and know what to leave out.

  • terraform
  • automation
  • tutorials

Flag configuration is the last piece of production most teams still change by clicking. Your infrastructure is declared, reviewed, and reproducible. The targeting rule deciding which 10% of users hit the new checkout was typed into a dashboard on a Tuesday by someone who has since left.

Managing feature flags as code closes that gap, and the Featureflip Terraform provider is how you do it. Projects, environments, flags and their variations, segments, SDK keys, and per-environment targeting are all declarable in .tf files and applied through the same pipeline as the rest of your infrastructure. This guide covers the install, a working configuration, importing flags you already have, and the more interesting question underneath: which parts of a flag belong in code, and which parts should never go there.

Key Takeaways

  • The provider manages six resource types through the public Management API, so flag structure gets the same review and history as your VPC.
  • Declaring a Boolean flag is four lines. Variations are auto-seeded, so you only spell them out for String, Number, and Json flags.
  • rules is authoritative. Once Terraform manages a flag-environment’s rules, it removes any rule added in the dashboard on the next apply.
  • Keep day-to-day toggling out of Terraform. A kill switch during an incident should not wait on a plan and apply.
  • Existing flags import cleanly with <project>/<flag-key>, so adoption is incremental rather than a rebuild.

1. Why flag config drifts out of review

A feature flag platform is configuration that changes behaviour in production, which makes it infrastructure by every definition except the one your review process uses. The result is a familiar asymmetry. Changing a load balancer takes a pull request and two approvals. Changing which users see an unreleased checkout flow takes a dropdown and a save button.

That asymmetry is fine for the thing flags are for. Flipping a flag off during an incident has to be fast, and routing that through CI would defeat the point. It stops being fine for the surrounding structure: which flags exist, what variations they carry, which segments they target, and what the rollout rules actually say. That structure is long-lived and it accretes. Nobody can diff a dashboard.

2. Install and authenticate

The provider is published on the Terraform Registry as canopy-labs/featureflip.

terraform {
required_providers {
featureflip = {
source = "canopy-labs/featureflip"
version = "~> 0.1"
}
}
}
provider "featureflip" {
organization = "acme-corp"
# token via FEATUREFLIP_TOKEN (recommended), or: token = var.featureflip_token
}

Authentication uses the same bearer token as the Management API. Create one in the dashboard under Organization Settings, since tokens cannot be minted through the public API:

Set it through FEATUREFLIP_TOKEN rather than putting it in configuration.

3. Declare a project and your first flags

A Boolean flag is four required attributes. Variations are seeded automatically, so there’s nothing to spell out:

resource "featureflip_project" "web" {
key = "web"
name = "Web App"
description = "Customer-facing web application"
}
resource "featureflip_feature_flag" "new_checkout" {
project = featureflip_project.web.key
key = "new-checkout"
name = "New Checkout Flow"
type = "Boolean"
tags = ["checkout"]
}

Non-Boolean flags declare their variations inline. The type attribute is immutable, so a flag cannot be converted from Boolean to String later without being replaced:

resource "featureflip_feature_flag" "banner_text" {
project = featureflip_project.web.key
key = "banner-text"
name = "Banner Text"
type = "String"
variations = [
{ key = "control", name = "Control", value = "Welcome!" },
{ key = "variant", name = "Variant", value = "Hi there!" },
]
}

For Json flags, value holds the JSON document as a string. Variation keys are immutable too, which matters because your SDK calls reference them.

4. Targeting rules and per-environment state

featureflip_flag_environment is where a flag becomes real. It carries the on/off state for one environment, the fallthrough variation, prerequisites, and the targeting rules. List position sets rule priority:

resource "featureflip_segment" "beta_users" {
project = featureflip_project.web.key
key = "beta-users"
name = "Beta Users"
conditions = [
{ attribute = "email", operator = "EndsWith", values = ["@acme.io"] },
]
}
resource "featureflip_flag_environment" "new_checkout_prod" {
project = featureflip_project.web.key
flag = featureflip_feature_flag.new_checkout.key
environment = "production"
enabled = true
default_variation = "false"
rules = [
{
description = "Beta users always get the new checkout"
variation = "true"
segment = featureflip_segment.beta_users.key
},
{
description = "50% rollout in Germany"
variation = "true"
rollout_percentage = 50
condition_groups = [
{
operator = "And"
conditions = [
{ attribute = "country", operator = "Equals", values = ["DE"] },
]
},
]
},
]
}

Reviewing that diff tells you something a dashboard screenshot never will: the rollout went from 10 to 50 percent, someone approved it, and the commit says why.

5. Draw the line at toggling

This is the part worth getting right before you adopt the provider widely.

Which parts of a feature flag belong in Terraform Two columns. Terraform owns the structural, long-lived configuration: projects, environments, flag definitions and variations, segments, SDK keys, and targeting rules. The dashboard and API own fast, short-lived actions: flipping a flag off during an incident, and nudging a rollout percentage mid-ramp. Terraform owns it structural, long-lived, worth reviewing Projects and environments Flag definitions and variations Segments Targeting rules SDK keys Dashboard or API owns it fast, short-lived, needs no review Killing a flag during an incident Nudging a ramp mid-rollout Flipping a flag on in dev A plan and apply is the slower path to a kill switch.
The split that holds up in practice. Structure is reviewed, toggling stays fast.

Turning a flag off during an incident is a dashboard or API action. Sending it through a plan and apply adds minutes to your recovery time for no benefit, and the kill switch exists precisely because those minutes matter. The pattern most teams settle on is to manage projects, environments, flags, and segments in Terraform, then leave day-to-day toggling alone.

Targeting sits in between, and it comes with a rule you need to know before you adopt it.

6. rules is authoritative

Once you set rules on a featureflip_flag_environment, Terraform owns every rule in that flag-environment. A rule someone adds through the dashboard is removed on the next apply, without a prompt beyond the plan output.

That’s the correct behaviour for declarative infrastructure, and it’s still a sharp edge if half your team hasn’t been told. Two ways to live with it: put targeting under Terraform for the flags whose rules are stable and reviewed, and leave rules unset on the flags your team iterates on daily. Leaving it unset means Terraform manages the flag’s existence and its on/off state while ignoring the rules entirely.

Destroying a featureflip_flag_environment deletes its managed rules and disables the flag in that environment. The fallthrough configuration stays in place.

7. Import the flags you already have

Nobody adopts this on a greenfield project. Both resources import with a path-style ID:

Terminal window
terraform import featureflip_feature_flag.new_checkout web/new-checkout
terraform import featureflip_flag_environment.new_checkout_prod web/new-checkout/production

The flag-environment import brings in identifiers only. Rules and prerequisites are not pulled into state, so add them to your configuration explicitly if you want Terraform to manage them. That’s a deliberate choice: it means importing a flag can’t silently hand Terraform ownership of targeting you hadn’t reviewed.

Import one flag, run a plan, confirm it’s empty, then widen. A plan that comes back empty is the signal that your configuration matches reality.

8. Two things that bite

SDK keys are sensitive, and destroying one revokes it. The plaintext value of a featureflip_sdk_key is available only at creation and is stored in Terraform state afterwards, so treat that state as a secret. Destroying the resource revokes the key, which cannot be undone, and any service still holding it stops evaluating.

resource "featureflip_sdk_key" "backend" {
project = featureflip_project.web.key
environment = "production"
name = "backend"
type = "ServerSide"
}
output "sdk_key" {
value = featureflip_sdk_key.backend.key
sensitive = true
}

Flag type and variation keys are immutable. Changing either forces replacement, and replacing a flag your code already reads is a production change rather than a config tweak. Decide the type when you create the flag.


The shorter version

The Featureflip Terraform provider manages projects, environments, flags and variations, segments, SDK keys, and per-environment targeting through the public Management API. Install it from the Registry as canopy-labs/featureflip, authenticate with a service token through FEATUREFLIP_TOKEN, and declare a Boolean flag in four attributes. Managing feature flags as code works best on the structural pieces, where review and history are worth having. Leave incident-time toggling to the dashboard, because a plan and apply is the slower path to a kill switch. Remember that rules is authoritative once set, and import existing flags with <project>/<flag-key> so adoption is incremental.


Frequently asked questions

Is there a Terraform provider for feature flags?

Yes. Featureflip publishes one on the Terraform Registry as canopy-labs/featureflip. It manages projects, environments, feature flags and their variations, user segments, SDK keys, and per-environment state and targeting rules, all through the public Management API. Install it with a standard required_providers block and authenticate with a service token.

Should feature flags be managed as code?

The structural parts, yes. Managing feature flags as code pays off for the configuration that outlives any one release. Which flags exist, what variations they carry, which segments they target, and what the targeting rules say are all long-lived configuration that benefits from review, history, and reproducible environments. The exception is toggling. Turning a flag off during an incident needs to be immediate, and routing it through a plan and apply adds minutes to recovery for no gain. Manage structure in Terraform and leave the fast actions to the dashboard or API.

What happens if someone edits a Terraform-managed flag in the dashboard?

It depends on the attribute. Once rules is set on a featureflip_flag_environment, Terraform owns every rule there and removes dashboard-created rules on the next apply. If you leave rules unset, Terraform manages the flag’s existence and state while ignoring its rules, which is the right choice for flags your team iterates on daily.

Can I import feature flags I already created?

Yes, and this is the normal adoption path. terraform import featureflip_feature_flag.example <project>/<flag-key> brings an existing flag under management, and flag-environments import with <project>/<flag>/<environment>. The flag-environment import brings in identifiers only, so rules and prerequisites stay unmanaged until you add them to your configuration.

Does the Terraform provider evaluate flags?

No. It manages configuration, the same surface the dashboard and the Management API act on. Reading a flag’s value for a user at runtime is the job of the SDKs, which evaluate locally in memory on your request path. Configuration and evaluation stay on separate surfaces, which is what keeps flag reads fast.


Featureflip is a flat-priced feature flag platform, and flags-as-code is included rather than reserved for an enterprise tier: the Terraform provider and the public Management API work on every plan, alongside the MCP server if you’d rather drive flags from your editor. See the provider docs for the full resource reference, or start on the free Solo plan without a credit card.