Infrastructure as Code Export
FeatureSignals supports an Infrastructure as Code (IaC) approach to feature flag management. This allows you to define flags, environments, segments, and targeting rules in declarative configuration files, enabling version control, code review, and automated provisioning.
Why IaC for Feature Flags?
Using IaC for feature flag management provides several benefits:
- Version control: All flag configurations are stored in your repository alongside your application code
- Code review: Changes to flags go through the same PR review process as code changes
- Audit trail: Git history provides a complete record of who changed what and when
- Reproducibility: Provision identical flag configurations across environments (dev, staging, production)
- Disaster recovery: Re-create your entire flag configuration from source control
- Migration: Export from other providers and import into FeatureSignals as YAML/JSON
Configuration Format
FeatureSignals IaC configurations are written in YAML or JSON. The format mirrors the domain model:
Project Configuration
# project.yaml
project:
name: "My Application"
description: "Feature flags for My Application"
environments:
- name: "Development"
slug: "dev"
color: "#00FF00"
- name: "Staging"
slug: "staging"
color: "#FFA500"
- name: "Production"
slug: "production"
color: "#FF0000"
Feature Flag Configuration
# flags.yaml
flags:
- key: "new-checkout"
name: "New Checkout Flow"
description: "Enable the redesigned checkout experience"
flag_type: "boolean"
category: "release"
default_value: false
tags: ["checkout", "ux", "experiment"]
states:
- environment: "Development"
enabled: true
percentage_rollout: 10000
rules:
- priority: 0
description: "Internal users only"
conditions:
- attribute: "email"
operator: "endsWith"
values: ["@company.com"]
value: true
match_type: "any"
- environment: "Production"
enabled: false
percentage_rollout: 0
- key: "api-timeout"
name: "API Timeout Configuration"
description: "Configure the API timeout value"
flag_type: "number"
category: "ops"
default_value: 3000
tags: ["api", "performance"]
states:
- environment: "Production"
enabled: true
default_value: 5000
Segment Configuration
# segments.yaml
segments:
- key: "beta-users"
name: "Beta Users"
description: "Users enrolled in the beta program"
match_type: "any"
rules:
- attribute: "email"
operator: "endsWith"
values: ["@beta.example.com"]
- attribute: "tier"
operator: "in"
values: ["beta", "early-access"]
- key: "enterprise-customers"
name: "Enterprise Customers"
description: "Enterprise plan subscribers"
match_type: "all"
rules:
- attribute: "plan"
operator: "eq"
values: ["enterprise"]
- attribute: "seats"
operator: "gte"
values: ["50"]
Exporting IaC Configurations
Manual Export via API
You can export your current FeatureSignals configuration using the management API:
# Export all flags for a project
curl -X GET https://api.featuresignals.com/v1/projects/proj_abc123/flags/export \
-H "Authorization: Bearer YOUR_JWT" \
-o flags-export.yaml
# Export all segments
curl -X GET https://api.featuresignals.com/v1/projects/proj_abc123/segments/export \
-H "Authorization: Bearer YOUR_JWT" \
-o segments-export.yaml
# Export environments
curl -X GET https://api.featuresignals.com/v1/projects/proj_abc123/environments/export \
-H "Authorization: Bearer YOUR_JWT" \
-o environments-export.yaml
Using the CLI Tool
The FeatureSignals CLI provides export commands:
# Export everything
fsctl export --project proj_abc123 --format yaml --output ./config
# Export specific resources
fsctl export flags --project proj_abc123 --format json --output ./flags.json
fsctl export segments --project proj_abc123 --format yaml --output ./segments.yaml
fsctl export environments --project proj_abc123 --format yaml --output ./environments.yaml
# Export with filters
fsctl export flags --project proj_abc123 --tag "production-ready" --output ./production-flags.yaml
Automated Export Pipeline
For continuous backup and version control, set up an automated export:
# .github/workflows/export-flags.yml
name: Export Feature Flags
on:
schedule:
- cron: '0 6 * * *' # Daily at 6 AM
workflow_dispatch: # Manual trigger
jobs:
export:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Export Feature Flags
run: |
fsctl export --project ${{ vars.FS_PROJECT_ID }} \
--format yaml \
--output ./config/flags
env:
FS_API_KEY: ${{ secrets.FS_API_KEY }}
FS_BASE_URL: ${{ vars.FS_BASE_URL }}
- name: Create Pull Request
uses: peter-evans/create-pull-request@v5
with:
commit-message: "chore: update feature flag export"
title: "Automated Feature Flag Export"
branch: "automated/flag-export"
Importing IaC Configurations
During Initial Migration
When migrating from another provider, export their IaC configuration and import it into FeatureSignals:
# Convert LaunchDarkly SDK configuration to FeatureSignals format
fsctl convert --from launchdarkly --input ld-flags.json --format yaml --output fs-flags.yaml
# Import into FeatureSignals
fsctl import --project proj_abc123 --file fs-flags.yaml
Bulk Import via API
curl -X POST https://api.featuresignals.com/v1/projects/proj_abc123/flags/import \
-H "Authorization: Bearer YOUR_JWT" \
-H "Content-Type: application/yaml" \
--data-binary @flags.yaml
Terraform Provider
For teams using Terraform, the FeatureSignals Terraform provider allows managing flags as resources:
# main.tf
terraform {
required_providers {
featuresignals = {
source = "featuresignals/featuresignals"
version = "~> 1.0"
}
}
}
provider "featuresignals" {
api_key = var.fs_api_key
base_url = var.fs_base_url
}
resource "featuresignals_project" "my_app" {
name = "My Application"
}
resource "featuresignals_environment" "production" {
project_id = featuresignals_project.my_app.id
name = "Production"
slug = "production"
color = "#FF0000"
}
resource "featuresignals_flag" "new_checkout" {
project_id = featuresignals_project.my_app.id
key = "new-checkout"
name = "New Checkout Flow"
flag_type = "boolean"
category = "release"
default_value = false
state {
environment_id = featuresignals_environment.production.id
enabled = false
percentage_rollout = 0
}
}
resource "featuresignals_segment" "beta_users" {
project_id = featuresignals_project.my_app.id
key = "beta-users"
name = "Beta Users"
match_type = "any"
rule {
attribute = "email"
operator = "endsWith"
values = ["@beta.example.com"]
}
}
Migration IaC Patterns
Pattern 1: Gradual Cutover
Migrate flags in phases, keeping both systems running:
# Phase 1: Import flags as disabled (shadow mode)
flags:
- key: "new-checkout"
source_key: "ld_new_checkout" # Original LD key for reference
states:
- environment: "Production"
enabled: false # Shadow mode — not serving traffic yet
# Phase 2: Enable for test users
flags:
- key: "new-checkout"
states:
- environment: "Production"
enabled: true
rules:
- conditions:
- attribute: "email"
operator: "endsWith"
values: ["@company.com"]
value: true
# Phase 3: Full rollout
flags:
- key: "new-checkout"
states:
- environment: "Production"
enabled: true
percentage_rollout: 10000
Pattern 2: Dual-Read Verification
During migration, read from both systems and compare:
import { FeatureSignalsClient } from '@featuresignals/node';
import { LDClient } from 'launchdarkly-node-server-sdk';
async function dualRead(flagKey: string, context: any) {
const fsValue = await fsClient.boolVariation(flagKey, context, false);
const ldValue = await ldClient.boolVariation(flagKey, context, false);
if (fsValue !== ldValue) {
console.warn(`Mismatch for flag ${flagKey}: FS=${fsValue} LD=${ldValue}`);
// Log to monitoring system
await reportMismatch(flagKey, context, fsValue, ldValue);
}
// Return the LD value until cutover is complete
return ldValue;
}
Pattern 3: Flag Key Mapping
When flag keys differ between systems, maintain a mapping:
# key-mapping.yaml
mappings:
- source_provider: "launchdarkly"
source_key: "enable-new-checkout"
fs_key: "new-checkout"
notes: "Renamed for consistency with naming conventions"
- source_provider: "unleash"
source_key: "app.new-checkout"
fs_key: "new-checkout"
notes: "Removed namespace prefix"
- source_provider: "flagsmith"
source_key: "new_checkout_feature"
fs_key: "new-checkout"
notes: "Converted from snake_case to kebab-case"
Version Control Best Practices
Repository Structure
config/
├── environments/
│ ├── development.yaml
│ ├── staging.yaml
│ └── production.yaml
├── flags/
│ ├── checkout.yaml
│ ├── search.yaml
│ └── payment.yaml
├── segments/
│ ├── beta.yaml
│ └── enterprise.yaml
├── key-mapping.yaml
└── project.yaml
CI/CD Pipeline Integration
# .github/workflows/deploy-flags.yml
name: Deploy Feature Flags
on:
push:
branches: [main]
paths:
- 'config/**/*.yaml'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Validate configuration
run: fsctl validate ./config
deploy:
needs: validate
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Apply configuration
run: fsctl apply ./config --project ${{ vars.FS_PROJECT_ID }}
env:
FS_API_KEY: ${{ secrets.FS_API_KEY }}
Validation
Always validate IaC configurations before applying:
# Validate syntax
fsctl validate config/flags.yaml
# Validate against schema
fsctl validate --schema v1 config/flags.yaml
# Dry-run apply (show changes without making them)
fsctl apply --dry-run config/flags.yaml --project proj_abc123
# Diff between current and desired state
fsctl diff config/flags.yaml --project proj_abc123
Advanced IaC Patterns
Templating with Environment Variables
# flags.yaml.template
flags:
- key: "api-endpoint"
name: "API Endpoint URL"
flag_type: "string"
default_value: "${DEFAULT_API_URL}"
states:
- environment: "Development"
default_value: "${DEV_API_URL}"
- environment: "Production"
default_value: "${PROD_API_URL}"
Render with your preferred tool:
# Using envsubst
envsubst < flags.yaml.template > flags.yaml
# Using yq
yq eval '.flags[0].default_value = strenv(DEFAULT_API_URL)' flags.yaml.template > flags.yaml
Multi-Project Management
# projects.yaml
projects:
- name: "Frontend App"
environments: ["dev", "staging", "production"]
flags:
- key: "new-ui"
name: "New UI"
type: "boolean"
default: false
- name: "Backend API"
environments: ["dev", "staging", "production"]
flags:
- key: "new-algorithm"
name: "New Recommendation Algorithm"
type: "boolean"
default: false
Converting Between Providers
The fsctl convert command translates configurations between feature flag platforms:
# LaunchDarkly → FeatureSignals
fsctl convert \
--from launchdarkly \
--input launchdarkly-export.json \
--output fs-flags.yaml \
--format yaml
# Unleash → FeatureSignals
fsctl convert \
--from unleash \
--input unleash-features.json \
--output fs-flags.yaml \
--format yaml
# Flagsmith → FeatureSignals
fsctl convert \
--from flagsmith \
--input flagsmith-export.json \
--output fs-flags.yaml \
--format yaml
Limitations
- Identity overrides: Per-user identity overrides from Flagsmith are not included in IaC exports. These must be managed through the API or dashboard.
- Audit history: IaC exports do not include audit log history. Use the audit export API for compliance purposes.
- Webhook configurations: Webhook and integration configurations must be managed separately.
- Complex nested segments: Deeply nested segment rules (Flagsmith
ANY/ALLnesting) are simplified in the YAML format. - Percentage rollouts: Percentage-based rollouts are exported as-is. The deterministic hashing algorithm may produce slightly different splits between providers.
Next Steps
- Migration Overview — understand the full workflow
- Migrate from LaunchDarkly — step-by-step guide
- Migrate from Unleash — step-by-step guide
- Migrate from Flagsmith — step-by-step guide
- Migration Troubleshooting — common issues and solutions