Skip to main content

Overview

The Secret resource stores sensitive values such as database passwords and API keys in AWS Secrets Manager. Each secret it creates is encrypted with its own Customer Managed Key (CMK), and accepts a generated password, a JSON object of SecretValue references, or a plain string. Fjall creates these secrets for you inside the database, compute and pattern constructs. Declare one directly when an application needs a credential no other construct owns.

Resource Class

The deep lib/... path is a published subpath of the package. Secrets constructs are not re-exported from the package root, so this import is the only one that compiles.
This page covers using the construct class directly. The scaffolded fjall/<app>/infrastructure.ts that fjall create app writes never does that: it imports App and the *Factory symbols from the package root, and fjall add edits it through the codemod engine. For runtime secrets you set yourself, use fjall secrets, which writes SSM Parameter Store SecureStrings rather than Secrets Manager entries.

Basic Usage

Generated Password

Prefer this shape. The value is generated inside AWS and never appears in your CDK source, the synthesised template, or any deploy artefact.

JSON Secret from Existing Values

Use secretObjectValue with SecretValue references when the value already lives somewhere secure, such as an SSM SecureString parameter.

Plain String (Escape Hatch)

secretStringValue is wrapped in SecretValue.unsafePlainText, so the value is embedded verbatim in the synthesised CloudFormation template and every synth artefact. The construct raises a synth-time warning (@fjall/components-infrastructure:secrets:plainTextSecretValue) whenever you use it. Anyone with read access to the template, the deploy artefact, or the CloudFormation stack can recover the value.Use generateSecretString, or secretObjectValue with SecretValue.ssmSecure(...), instead.
secretStringValue wins over generateSecretString. Supplying both silently ignores the generator.

Configuration Options

Core Properties

KMS aliases are unique per account and region. If your construct id is not account-unique (a fixed-name id inside a pattern deployed once per application, for example), pass an application-scoped aliasName or the second deployment fails.

Secret Value Options (use one)

Importing an Existing Secret

importExisting: true changes the construct’s behaviour: it references the secret by name and creates no CMK, so secretsCustomerManagedKey stays undefined. Every value option is ignored.

Default Features

Every secret the construct creates gets:
  • KMS encryption with its own Customer Managed Key
  • Automatic key and alias creation, alias cmk/${id}
  • Key rotation enabled on the CMK
  • getImport() for cross-stack consumption

CMK Removal Policy

The CMK is minted with protects: "stack-scoped", which resolves through the env-aware default rather than an unconditional RETAIN: The 30-day pending window matches Secrets Manager’s own 30-day recovery window, so a secret deleted with its stack stays recoverable for as long as the key does. See KMS Key for the full protects contract.

Generated Passwords

Excluding Characters

Database Password with Username

API Key Generation

Consuming a Secret from Another Stack

getImport(field?) returns a SecretImport, the shape every Fjall compute construct accepts in its secretsImport map.

The SecretImport Shape

Supply exactly one of name or arn. Hand-write an import when the secret is managed outside your stack:

Deploy-Time ARN Resolution

resolveImportedSecret(scope, id, secretImport) turns a SecretImport into an ISecret whose ARN carries the AWS 6-character suffix. It resolves in three steps:
  1. An explicit arn on the import.
  2. The fjallResolvedSecretArns context map, which @fjall/deploy-core writes at deploy time after a DescribeSecret on every name.
  3. Secret.fromSecretNameV2 as an offline cdk synth fallback.
Step 3 renders a suffixless partial ARN, which real Secrets Manager rejects with AccessDenied at task launch. A Fjall deploy always runs step 2, so the suffixless shape never reaches a deployed task definition. Do not hand-roll fromSecretNameV2 in your own constructs.

Integration with Resources

With RDS

Database constructs create and own their credentials secret. Read it with getCredentials(), which returns the same Secret wrapper.

With ECS

Pass imports through a container’s secretsImport map. Fjall resolves each one and injects it as an environment variable at task launch.

With Lambda

LambdaFunction takes the same map.
Working against raw CDK instead, grant read access on the underlying ISecret:

KMS Encryption

Default CMK Creation

Access the CMK

secretsCustomerManagedKey is optional, because a secret created with importExisting: true has no key. Narrow it before use, or strictNullChecks rejects the read.

Secret Rotation

Attach CDK’s SecretRotation to the underlying ISecret. Database constructs do this for you.

Factory Pattern

Secret.build returns a factory that a StackBuilder invokes.

IAM Permissions

Grant Read Access

grantRead also grants kms:Decrypt on the CMK through the Secrets Manager service principal, so no separate key grant is needed.

Custom Permissions

Outputs

The nested CustomerManagedKey construct emits both CloudFormation outputs. Their names key off the CMK construct id, ${id}CustomerManagedKey, run through toPascalCase. Call that name.
Export names carry the emitting stack’s name as a prefix, because CloudFormation export names are unique per account and region. The output key is not prefixed. Build a cross-stack import from Stack.of(this).stackName, never from the bare ${name}KeyArn string.
For a Secret with id ApiKey in a stack named AcmeApiStorage:

Complete Example

Best Practices

  1. Generate values in AWS. Reach for generateSecretString first, SecretValue.ssmSecure second, secretStringValue never.
  2. Inject through secretsImport, not environment. Environment variables land in the task definition in clear text.
  3. Import a single field with getImport("password") rather than handing a whole JSON blob to a container.
  4. Pass an application-scoped aliasName whenever the construct id repeats across applications in one account and region.
  5. Rotate database credentials on a 30 to 90 day schedule.
  6. Apply least-privilege IAM. Grant on the exact secret ARN, not a wildcard.
  7. Separate secrets by purpose so a single grant does not expose unrelated credentials.
  8. Monitor access with CloudTrail.

Cost

AWS list price, us-east-1:
  • $0.40 per secret per month
  • $0.05 per 10,000 API calls
  • 1permonthfortheCMK,risingtoabout1** per month for the CMK, rising to about **3 as key rotation mints its first two versions

Cost Saving Tips

Combine related values into one JSON secret rather than minting a secret per field. Each secret carries its own monthly charge and its own CMK.
Cache resolved values in long-lived Lambda handlers so repeated invocations do not re-charge per API call, and keep non-sensitive configuration in SSM Parameter Store standard parameters, which are free.

Security Considerations

Least-Privilege Access

Non-Production Key Deletion

Outside production, the CMK resolves to DESTROY. Deleting a staging stack schedules the key for deletion with a 30-day window, after which anything still encrypted with it becomes permanently unreadable. If a secret must survive its stack, create its key separately with protects: "outlives-stack".

Troubleshooting

Next Steps

KMS Key

Configure the customer-managed key that encrypts each secret.

IAM Role

Grant roles least-privilege read access to secrets.

RDS Instance

Manage database credentials with generated secrets.

ECS Cluster

Inject secrets into container tasks with secretsImport.

fjall secrets

Manage application secrets from the CLI in SSM Parameter Store.

Lambda Function

Inject secrets into Lambda handlers at runtime.