A PowerShell script that identifies LaunchDarkly feature flags ready for code removal or archival based on configurable rules. Designed for GitHub Actions integration.
- PowerShell 7.0+
- LaunchDarkly API Access Token (set as
LD_ACCESS_TOKENenvironment variable) - LaunchDarkly CLI
- Save the LD API key as an environment variable
$env:LD_ACCESS_TOKEN = <LD_API_KEY> - Determine the best method for providing the LD project and environment key(s).
2a. For single project or if all projects use the same production environment key(s), you can use the command line input arguments
2b. If you want to use the script with multiple projects where each project might have different environment(s) that need to be scanner, use the JSON configuration file (example in
config/proj-env-config-example.json). - Execute the script, i.e.:
pwsh ./scripts/ld-flag-cleanup.ps1 -ConfigFile "./config/proj-env-config-example.json" -Verbose
The script accepts either command-line parameters or a JSON configuration file:
pwsh ./scripts/ld-flag-cleanup.ps1 -Projects "project1,project2" -Environments "production,staging" -VerboseCommand Line Limitation: When using command-line parameters, all specified environments are applied to all specified projects.
pwsh ./scripts/ld-flag-cleanup.ps1 -ConfigFile "./config/projects-config.json" -VerboseJSON Configuration Format:
{
"projects": [
{
"projectKey": "web-platform",
"environments": ["production", "staging", "development"]
},
{
"projectKey": "mobile-app",
"environments": ["production", "staging"]
}
],
"globalSettings": {
"rulesPath": "./config/cleanup-rules.yaml",
"outputDirectory": "./artifacts"
}
}Reference Example: See config/proj-env-config-example.json for an example.
Edit config/cleanup-rules.yaml to customize cleanup criteria:
# Time-based thresholds
daysSinceLastEvaluation: 7 # <number> flags with no evaluations in past N days
daysSinceCreation: 30 # <number> flags created more than N days ago
# Flag requirements
checkForCodeReferences: true # <boolean> whether to check for existence of flags' code references
checkForFlagType: true # <boolean> whether to consider the flag type (temporary/permanent)
checkForFlagStatus: true # <boolean> whether to check the flag status (new/active/launched/inactive)
# Debug output
printDebugLogs: 3 # <number> whether the print debug logs + a number of flags to show debug info for (0 = no debug logs)- Created more than X days ago
- Has evaluations in the past Y days
- Flag type = temporary (if enabled)
- Flag status = launched (if enabled)
- Has code references (if enabled)
Example ldcli command for code removal flags:
ldcli flags list \
--project "your-project-key" \
--env "production" \
--expand "codeReferences,evaluation" \
--filter "filterEnv:production,creationDate:{\"before\":1756047090193},type:temporary,evaluated:{\"after\":1755442290193}" \
--limit 100 \
-o json- Created more than X days ago
- No evaluations in the past Y days
- Flag type = temporary (if enabled)
- Flag status = inactive (if enabled)
- No code references (if enabled)
Example ldcli command for archival flags:
ldcli flags list \
--project "your-project-key" \
--env "production" \
--expand "codeReferences,evaluation" \
--filter "filterEnv:production,creationDate:{\"before\":1756047090193},type:temporary" \
--limit 100 \
-o jsonNote: The timestamps in the examples are dynamically calculated based on your daysSinceCreation and daysSinceLastEvaluation rules. The script handles this automatically.
When multiple environments are specified for a project, the script aggregates data across all environments:
- Status: Flag is considered "launched" if any environment is launched, "inactive" if all are inactive
- Last Requested: Uses the most recent evaluation date across all environments
- Code References: Flag has code references if any environment shows references
The script generates three files in the artifacts directory:
Detailed machine-readable report with all flag data and decisions.
Tabular format for analysis and filtering.
Markdown summary suitable for GitHub PR comments, grouped by project.
Required: Set up the LaunchDarkly API token as a repository secret:
- Go to your repository → Settings → Secrets and variables → Actions
- Click "New repository secret"
- Name:
LD_ACCESS_TOKEN - Value: Your LaunchDarkly API token (starts with
api-)
name: LaunchDarkly Flag Cleanup Report
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
ld-cleanup:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Setup PowerShell
uses: PowerShell/PowerShell@v1
with:
pwsh-version: '7.4.x'
- name: Install LaunchDarkly CLI
run: npm install -g @launchdarkly/ldcli
- name: Run cleanup
env:
LD_ACCESS_TOKEN: ${{ secrets.LD_ACCESS_TOKEN }}
run: |
pwsh ./scripts/ld-flag-cleanup.ps1 `
-ConfigFile "./config/proj-env-config-example.json" `
-Verbose
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: ld-cleanup-report
path: artifacts/*
- name: Comment PR summary
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const body = fs.readFileSync('artifacts/pr-summary.txt', 'utf8');
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body
}); - name: Run cleanup
env:
LD_ACCESS_TOKEN: ${{ secrets.LD_ACCESS_TOKEN }}
run: |
pwsh ./scripts/ld-flag-cleanup.ps1 `
-Projects "web,api" `
-Environments "staging,production" `
-VerboseNote: Command-line approach applies all environments to all projects (see limitations above).
Authentication Error:
Error: LD_ACCESS_TOKEN is not set
Set your API token: $env:LD_ACCESS_TOKEN = "your-token"
Configuration Error:
Error: Failed to load configuration file
Check JSON syntax and file permissions
No Flags Found:
Processing complete. Total flags processed: 0/0
Verify project keys and environment names are correct