AWS Parameter Store
AWS Systems Manager Parameter Store provides hierarchical secret storage with path-based organization. It's a cost-effective alternative to AWS Secrets Manager for simpler use cases.
Quick Start
# 1. Configure provider in fnox.toml
cat >> fnox.toml << 'EOF'
[providers]
ps = { type = "aws-ps", region = "us-east-1", prefix = "/myapp/prod/" }
EOF
# 2. Create parameter in AWS
aws ssm put-parameter \
--name "/myapp/prod/database-url" \
--value "postgresql://prod.example.com/db" \
--type "SecureString"
# 3. Reference in fnox.toml
cat >> fnox.toml << 'EOF'
[secrets]
DATABASE_URL = { provider = "ps", value = "database-url" } # With prefix, fetches "/myapp/prod/database-url"
EOF
# 4. Fetch secret
fnox get DATABASE_URLPrerequisites
- AWS account
- AWS credentials configured (CLI, environment variables, or IAM role)
- IAM permissions (see below)
IAM Permissions
Read-Only Access (Minimum)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DescribeParameters",
"Effect": "Allow",
"Action": "ssm:DescribeParameters",
"Resource": "*"
},
{
"Sid": "ReadParameters",
"Effect": "Allow",
"Action": ["ssm:GetParameter", "ssm:GetParameters"],
"Resource": "arn:aws:ssm:REGION:ACCOUNT:parameter/myapp/*"
}
]
}DescribeParameters Permission
The ssm:DescribeParameters action must use "Resource": "*" and cannot be scoped to specific ARNs.
Full Access (For Testing)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DescribeParameters",
"Effect": "Allow",
"Action": "ssm:DescribeParameters",
"Resource": "*"
},
{
"Sid": "ParameterStorePermissions",
"Effect": "Allow",
"Action": [
"ssm:GetParameter",
"ssm:GetParameters",
"ssm:PutParameter",
"ssm:DeleteParameter"
],
"Resource": ["arn:aws:ssm:REGION:ACCOUNT:parameter/myapp/*"]
}
]
}Configuration
Configure AWS Credentials
Choose one:
Option 1: Environment Variables
export AWS_ACCESS_KEY_ID="AKIA..."
export AWS_SECRET_ACCESS_KEY="..."
export AWS_REGION="us-east-1"Option 2: AWS CLI Profile
aws configure
# Or use named profile
export AWS_PROFILE=myappOption 3: IAM Role (Automatic on AWS)
If running on EC2, ECS, Lambda, or other AWS services:
# No configuration needed!
# Credentials are automatic via instance metadataConfigure fnox Provider
[providers]
ps = { type = "aws-ps", region = "us-east-1", prefix = "/myapp/prod/" } # prefix is optionalCreating Parameters
Via AWS CLI
# Create a SecureString parameter (encrypted)
aws ssm put-parameter \
--name "/myapp/prod/database-url" \
--value "postgresql://prod.db.example.com/mydb" \
--type "SecureString"
# Create with description
aws ssm put-parameter \
--name "/myapp/prod/api-key" \
--description "Production API key for external service" \
--value "sk_live_abc123xyz789" \
--type "SecureString"
# Update existing parameter
aws ssm put-parameter \
--name "/myapp/prod/api-key" \
--value "sk_live_newkey456" \
--type "SecureString" \
--overwriteVia fnox
# Store a secret directly via fnox
fnox set DATABASE_URL "postgresql://prod.db.example.com/mydb" --provider psVia AWS Console
- Go to AWS Systems Manager Console
- Click "Create parameter"
- Name it with your path (e.g.,
/myapp/prod/database-url) - Choose "SecureString" for sensitive values
- Enter the value
- Create
Referencing Parameters
Add references to fnox.toml:
[secrets]
DATABASE_URL = { provider = "ps", value = "database-url" } # → Fetches "/myapp/prod/database-url"
API_KEY = { provider = "ps", value = "api-key" } # → Fetches "/myapp/prod/api-key"
# Without prefix in provider, use full path like: value = "/myapp/prod/api-key"Usage
Get a Secret
fnox get DATABASE_URLRun Commands
# Fetches all secrets from Parameter Store
fnox exec -- ./start-server.shUse Different Profiles
# Different profile for different environments
fnox exec --profile production -- ./deploy.shPrefix Behavior
The prefix is prepended to the value:
[providers]
ps = { type = "aws-ps", prefix = "/myapp/prod/" }
[secrets]
DATABASE_URL = { provider = "ps", value = "database-url" } # → Fetches "/myapp/prod/database-url"
API_KEY = { provider = "ps", value = "api-key" } # → Fetches "/myapp/prod/api-key"Without prefix:
[providers]
ps = { type = "aws-ps" } # No prefix
[secrets]
DATABASE_URL = { provider = "ps", value = "/myapp/prod/database-url" } # → Full pathHierarchical Organization
Parameter Store supports path-based organization:
/myapp/
prod/
database/
url
password
api/
key
secret
staging/
database/
url
password[providers]
prod = { type = "aws-ps", region = "us-east-1", prefix = "/myapp/prod/" }
staging = { type = "aws-ps", region = "us-east-1", prefix = "/myapp/staging/" }
[secrets]
DATABASE_URL = { provider = "prod", value = "database/url" }
[profiles.staging.secrets]
DATABASE_URL = { provider = "staging", value = "database/url" }Multi-Environment Example
# Development: age encryption
[providers]
age = { type = "age", recipients = ["age1..."] }
[secrets]
DATABASE_URL = { provider = "age", value = "encrypted-dev-db..." }
# Staging: AWS Parameter Store
[profiles.staging.providers]
ps = { type = "aws-ps", region = "us-east-1", prefix = "/myapp/staging/" }
[profiles.staging.secrets]
DATABASE_URL = { provider = "ps", value = "database/url" }
# Production: AWS Parameter Store
[profiles.production.providers]
ps = { type = "aws-ps", region = "us-east-1", prefix = "/myapp/prod/" }
[profiles.production.secrets]
DATABASE_URL = { provider = "ps", value = "database/url" }# Development (local)
fnox get DATABASE_URL
# Staging
fnox get DATABASE_URL --profile staging
# Production
fnox get DATABASE_URL --profile productionCosts
AWS Parameter Store pricing:
- Standard parameters: Free (up to 10,000 parameters)
- Advanced parameters: $0.05 per parameter per month
- API calls: Free for standard tier
Cost Optimization
Parameter Store standard tier is free for most use cases. Use it for configuration values and simple secrets. Reserve AWS Secrets Manager for secrets that need automatic rotation.
Comparison: Parameter Store vs Secrets Manager
| Feature | Parameter Store | Secrets Manager |
|---|---|---|
| Cost | Free (standard tier) | $0.40/secret/month |
| Max Size | 4KB (8KB advanced) | 64KB |
| Rotation | Manual | Automatic |
| Versioning | Limited | Full versioning |
| Organization | Hierarchical paths (/a/b/c) | Flat with tags |
| Cross-account | Via Resource policies | Via Resource policies |
| Best For | Config values, simple secrets | Complex secrets, rotation |
Use Parameter Store when:
- You have simple secrets or configuration values
- Cost is a concern
- You want hierarchical organization
- You don't need automatic rotation
Use Secrets Manager when:
- You need automatic secret rotation
- You have complex JSON secrets
- You need full versioning history
CI/CD Example
GitHub Actions
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Deploy with secrets
run: |
fnox exec --profile production -- ./deploy.shPros
- ✅ Free for standard tier (up to 10,000 parameters)
- ✅ Hierarchical path-based organization
- ✅ IAM access control
- ✅ CloudTrail audit logs
- ✅ Secrets never in git
- ✅ Simple and straightforward
Cons
- ❌ No automatic rotation (use Secrets Manager for that)
- ❌ Limited versioning
- ❌ Smaller size limit (4KB standard, 8KB advanced)
- ❌ Requires AWS account and network access
- ❌ AWS vendor lock-in
Troubleshooting
"AccessDeniedException"
Check IAM permissions:
# Test access
aws ssm describe-parameters
aws ssm get-parameter --name "/myapp/prod/database-url" --with-decryption"ParameterNotFound"
Parameter doesn't exist. Check:
# List parameters with path
aws ssm get-parameters-by-path --path "/myapp/prod/" --recursive
# Check if prefix is correct in fnox.toml
cat fnox.toml | grep prefix"Invalid Region"
Verify region matches:
# Check fnox.toml region
cat fnox.toml | grep region
# Check AWS credentials region
echo $AWS_REGIONNext Steps
- AWS Secrets Manager - For automatic rotation and complex secrets
- AWS KMS - For encrypting secrets in git
- Real-World Example - Complete AWS setup
- Profiles - Multi-environment configuration