Skip to main content

Overview

AWS Secrets Manager stores sensitive data like database credentials, API keys, and tokens. Fjall automatically creates and manages secrets for database credentials when you use DatabaseFactory - you typically don’t need to create them manually.

Automatic Management

When you create databases with DatabaseFactory, Fjall automatically:
  • Generates strong passwords
  • Stores credentials in Secrets Manager
  • Grants access to compute resources
  • Injects secrets as environment variables
const database = app.addDatabase(
  DatabaseFactory.build("DB", {
    type: "Instance",
    databaseName: "mydb",
  })
);

// Secrets created automatically
// - Database password
// - Master username
// - Connection endpoint

Accessing Database Credentials

In ECS Containers

Use containerSecretsImport to inject secrets:
const api = app.addCompute(
  ComputeFactory.build("API", {
    type: "ecs",
    ecsType: "fargate",
    connections: [database],
    containerSecretsImport: {
      DB_HOST: database.getHostEndpoint(),
      DB_NAME: database.getDatabaseName(),
      DB_USER: database.getUsername(),
      DB_PASSWORD: database.getCredentials(),
    },
  })
);
These are available as environment variables in your container:
// In your application
const dbConfig = {
  host: process.env.DB_HOST,
  database: process.env.DB_NAME,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
};

In Lambda Functions

Access secrets via environment or direct retrieval:
const lambda = app.addCompute(
  ComputeFactory.build("Function", {
    type: "lambda",
    handler: "index.handler",
    runtime: Runtime.NODEJS_20_X,
    code: Code.fromAsset("./lambda"),
    environment: {
      SECRET_ARN: database.secret.secretArn,
    },
  })
);

// Grant read access
database.secret.grantRead(lambda.role);
Lambda code to retrieve:
import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";

const client = new SecretsManagerClient();

export const handler = async () => {
  const response = await client.send(
    new GetSecretValueCommand({
      SecretId: process.env.SECRET_ARN,
    })
  );
  
  const { username, password, host, dbname } = JSON.parse(response.SecretString);
  
  // Use credentials...
};

Custom Secrets

Create Application Secrets

For API keys, tokens, or other sensitive data:
import { Secret } from "aws-cdk-lib/aws-secretsmanager";

const apiKey = new Secret(this, "ApiKey", {
  secretName: "myapp/third-party-api-key",
  description: "Third party API key",
});

// Use in compute
const api = app.addCompute(
  ComputeFactory.build("API", {
    type: "ecs",
    containerSecretsImport: {
      API_KEY: ecs.Secret.fromSecretsManager(apiKey),
    },
  })
);

Store JSON Secrets

const config = new Secret(this, "Config", {
  secretName: "myapp/config",
  generateSecretString: {
    secretStringTemplate: JSON.stringify({
      api_url: "https://api.example.com",
      region: "us-east-1",
    }),
    generateStringKey: "api_key", // Generate this field
  },
});

Import Existing Secrets

Reference secrets created elsewhere:
import { Secret } from "aws-cdk-lib/aws-secretsmanager";

const existingSecret = Secret.fromSecretNameV2(
  this,
  "ExistingSecret",
  "prod/database/password"
);

// Use in application
database.secret.grantRead(lambda.role);

Secret Rotation

Automatic Database Rotation

Fjall-managed database secrets support rotation:
database.secret.addRotationSchedule("Rotation", {
  automaticallyAfter: Duration.days(30),
});

Custom Rotation

For non-database secrets, implement custom rotation:
import { Function, Runtime, Code } from "aws-cdk-lib/aws-lambda";

const rotationLambda = new Function(this, "RotationFunction", {
  runtime: Runtime.NODEJS_20_X,
  handler: "index.handler",
  code: Code.fromAsset("./rotation-lambda"),
});

apiKey.addRotationSchedule("Rotation", {
  rotationLambda,
  automaticallyAfter: Duration.days(90),
});

Granting Access

Read Access

// Grant Lambda function read access
database.secret.grantRead(lambda.role);

// Grant ECS task read access
apiKey.grantRead(api.taskRole);

Write Access

// For custom secret management
secret.grantWrite(updaterLambda.role);

DatabaseFactory Helper Methods

Convenient methods for database secrets:
// Get all methods
database.getHostEndpoint()   // Host endpoint secret
database.getDatabaseName()   // Database name secret
database.getUsername()       // Username secret
database.getCredentials()    // Password secret
database.getPort()           // Port secret

Retrieving Secrets in Code

Node.js Example

import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";

const client = new SecretsManagerClient();

async function getSecret(secretArn) {
  const response = await client.send(
    new GetSecretValueCommand({ SecretId: secretArn })
  );
  return JSON.parse(response.SecretString);
}

// Usage
const dbCredentials = await getSecret(process.env.DB_SECRET_ARN);

Python Example

import boto3
import json
import os

client = boto3.client('secretsmanager')

def get_secret(secret_arn):
    response = client.get_secret_value(SecretId=secret_arn)
    return json.loads(response['SecretString'])

# Usage
db_creds = get_secret(os.environ['DB_SECRET_ARN'])

Best Practices

  • Use DatabaseFactory helpers for database credentials
  • Use containerSecretsImport instead of environment variables for sensitive data
  • Never hardcode secrets in code or configuration files
  • Grant minimal permissions (read-only when possible)
  • Enable rotation for long-lived secrets
  • Use descriptive secret names like myapp/environment/purpose
  • Store secrets as JSON for multiple values
  • Leverage Fjall’s automatic management for databases

Cost Optimization

Secrets Manager Pricing

  • $0.40 per secret per month
  • $0.05 per 10,000 API calls

Reduce Costs

  • Cache secrets in Lambda/ECS (don’t fetch on every request)
  • Combine secrets in JSON format to reduce secret count
  • Use Parameter Store for non-sensitive configuration (free tier)
  • Delete unused secrets from old environments

Common Patterns

Multi-Environment Secrets

const env = process.env.ENVIRONMENT || "dev";

const apiKey = new Secret(this, "ApiKey", {
  secretName: `myapp/${env}/api-key`,
});

Shared Secrets

// One secret used by multiple services
const sharedKey = new Secret(this, "SharedKey", {
  secretName: "shared/encryption-key",
});

api.service.taskDefinition.taskRole.addToPolicy(
  new PolicyStatement({
    actions: ["secretsmanager:GetSecretValue"],
    resources: [sharedKey.secretArn],
  })
);

worker.function.addToRolePolicy(
  new PolicyStatement({
    actions: ["secretsmanager:GetSecretValue"],
    resources: [sharedKey.secretArn],
  })
);

Troubleshooting

Access Denied Errors

If services can’t read secrets:
  1. Check IAM permissions - Did you grant read access?
  2. Verify secret ARN - Is the environment variable correct?
  3. Check VPC endpoints - Lambda in VPC needs Secrets Manager endpoint
  4. Review resource policies - Any explicit denies?

Secret Not Found

  • Verify secret name/ARN is correct
  • Check the secret exists in the same region
  • Ensure secret hasn’t been deleted (check CloudTrail)

Connection Failures

For database secrets:
  • Verify connections array includes database
  • Check security groups allow traffic
  • Ensure secret contains correct credentials
  • Test with containerSecretsImport helper methods

See Also