From cloud-foundation-principles
Implements one-secret-per-service pattern for managing credentials, API keys, configs in secrets managers with KMS encryption, rotation, naming conventions, and account-based environment isolation.
How this skill is triggered — by the user, by Claude, or both
Slash command
/cloud-foundation-principles:secrets-and-configuration-managementThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
The most common infrastructure mistake with credentials is over-engineering the separation. Splitting database passwords into a secrets manager, endpoints into a parameter store, and feature flags into environment variables creates three access patterns, three IAM policies, and a Terraform apply every time you change a connection string.
The most common infrastructure mistake with credentials is over-engineering the separation. Splitting database passwords into a secrets manager, endpoints into a parameter store, and feature flags into environment variables creates three access patterns, three IAM policies, and a Terraform apply every time you change a connection string.
Put everything in one secret per service. One JSON blob containing every environment variable the service needs -- credentials, endpoints, flags, all of it. Store it in a secrets manager with customer-managed encryption. The application reads and parses the secret at startup. Done.
One secret per service. Each service has exactly one secret: /{service}/env. It contains a JSON object with every env var the service needs -- database passwords next to database hosts, API keys next to feature flags. No separation between "secrets" and "configuration."
Account = environment. The secret path is /{service}/env in every account. Dev account, prod account, staging account -- same path. The AWS account provides the isolation (see multi-account-from-day-one skill). Application code never needs to know which environment it runs in. This mirrors the internal DNS pattern (see network-architecture skill) where db.internal resolves to the right database per VPC -- same hostname everywhere, different values per environment.
Change without Terraform. Update the secret value in the console or CLI, force a container redeploy. No plan/apply cycle for config changes. Terraform creates the secret resource and sets the initial value; the team manages the value thereafter.
Customer-managed encryption. Every secret is encrypted with a KMS key you control, not the provider default. This enables key rotation, cross-account access policies, and decryption audit trails.
Rotation is a spectrum. Database credentials can auto-rotate if you invest in the rotation Lambda. Third-party API keys (Stripe, SendGrid) rarely rotate in practice. Don't let perfect rotation block shipping. Start with KMS encryption and proper access scoping; add rotation when it matters.
{
"DB_HOST": "mydb.cluster-xxx.eu-west-1.rds.amazonaws.com",
"DB_PORT": "5432",
"DB_PASSWORD": "auto-rotated-or-manual",
"REDIS_URL": "redis://cache.internal:6379",
"SENDGRID_API_KEY": "SG.xxx",
"FEATURE_V2_API": "true"
}
One JSON object. One secret ARN. One IAM policy statement. The application reads /{service}/env at startup, parses the JSON, and populates its environment.
# Good: one secret, app reads and parses at startup
resource "aws_secretsmanager_secret" "env" {
name = "/${var.service_name}/env"
kms_key_id = aws_kms_key.secrets.arn
}
resource "aws_ecs_task_definition" "myapp" {
# ...
container_definitions = jsonencode([{
name = var.service_name
environment = [{
name = "SECRET_NAME"
value = "/${var.service_name}/env"
}]
}])
}
# Bad: plaintext password in environment variable via Terraform
resource "aws_ecs_task_definition" "myapp" {
container_definitions = jsonencode([{
name = "myapp"
environment = [{
name = "DB_PASSWORD"
value = "hunter2" # Plaintext in state file, logs, and console
}]
}])
}
# Bad: separate stores for secrets and configuration
/prod/myapp/db-password --> Secrets Manager
/prod/myapp/db-host --> SSM Parameter Store
/prod/myapp/redis-url --> SSM Parameter Store
/prod/myapp/sendgrid-api-key --> Secrets Manager
# Two stores, two access patterns, two IAM policies,
# and a Terraform apply to change a connection string.
Dev account (123456789012):
/myapp/env (all env vars for myapp)
/myapp/db-app_rw (database role credential)
/myapp/db-analytics_ro (database role credential)
Prod account (987654321098):
/myapp/env (all env vars for myapp)
/myapp/db-app_rw (database role credential)
/myapp/db-analytics_ro (database role credential)
Same paths. Same code. Different accounts. Application code reads /{service}/env -- it never needs to know whether it runs in dev or prod.
Database role credentials are the exception to the one-blob rule. They are stored as individual secrets at /{service}/db-{role} because they are shared across consumers (app service, analytics service, RDS proxy) and auto-rotation targets individual secrets, not keys inside a blob.
The one-secret-per-service pattern covers everything an application needs at runtime. But cross-service infrastructure dependencies -- where one Terraform module's output feeds another module's input -- need a different mechanism.
Use SSM Parameter Store (or Terraform remote state) for infrastructure wiring:
These values change when infrastructure changes. They must flow through Terraform or a parameter store so dependent modules pick up the new value automatically. They are not application config -- they are infrastructure bindings.
# Infrastructure wiring: consumed by Terraform modules, not application code
resource "aws_ssm_parameter" "db_endpoint" {
name = "/${var.service_name}/infra/db-endpoint"
type = "String"
value = aws_db_instance.main.address
tags = local.tags
}
Database users follow the same principle. Never create person-specific database accounts (john_doe, jane_smith). Create role-based users that describe purpose and access level.
Use abbreviated access suffixes for brevity (see naming-and-labeling-as-code skill for the canonical naming conventions):
api_rw -- API service (read + write)
api_ro -- API service (read only, e.g., replica queries)
dashboard_ro -- Analytics/BI tools (read only, all tables)
migration_admin -- Schema migration runner (DDL permissions, time-boxed)
generic_ro -- All team members (read only, for debugging)
Individual developer access:
Developer --> SSO --> Cloud console --> Database proxy --> generic_ro role
(No personal credentials. Access revoked by disabling SSO account.)
Application access:
Container --> Reads /{service}/env --> Gets DB_PASSWORD --> Connects directly
(Password from secrets manager. No human knows the password.)
Analytics access:
BI tool --> Reads /{service}/db-analytics_ro --> Connects via proxy
(Scoped to SELECT on reporting tables only.)
Emergency admin access:
SRE --> SSO --> Temporary admin session --> migration_admin role
(Time-boxed to 1 hour. Fully audited. Requires approval.)
| Concept | AWS | GCP | Azure |
|---|---|---|---|
| Secrets storage | Secrets Manager | Secret Manager | Key Vault |
| Encryption keys | KMS (customer-managed CMK) | Cloud KMS | Key Vault (keys) |
| Auto-rotation | Rotation Lambdas | Rotation via Cloud Functions | Key Vault rotation policies |
| Infrastructure config (cross-service wiring) | SSM Parameter Store | Secret Manager labels or Firestore | App Configuration |
| Database proxy | RDS Proxy / IAM DB auth | Cloud SQL Auth Proxy | Microsoft Entra ID DB auth |
| Credential-free CLI | aws sso login | gcloud auth login | az login |
Working implementations in examples/:
examples/secrets-and-config-separation.md -- Complete single-secret-per-service setup with KMS encryption, one JSON blob, ECS task definition, and scoped IAM policiesexamples/database-role-management.md -- Role-based database user creation with purpose-named roles, individual secrets per role, and IAM-based developer access through a database proxyWhen designing or reviewing secrets and configuration management:
/{service}/env containing a JSON blob with all env vars/{service}/db-{role} (exception to one-blob rule){purpose}_{access}), not person-specific.env filesnpx claudepluginhub oborchers/fractional-cto --plugin cloud-foundation-principlesGuides designing secret storage, rotation, and credential management systems covering HashiCorp Vault patterns, AWS Secrets Manager, Azure Key Vault, and zero-knowledge architectures.
Guides secure secrets management using Vault, AWS Secrets Manager, Azure Key Vault, environment variables, rotation, scanning tools, and CI/CD security. For implementing storage, rotation, leak prevention, credentials review.
Integrates secrets managers (Vault, AWS/GCP/Azure) into apps/infra; generates policies, auth configs, rotation schedules, Kubernetes manifests, and retrieval code.