> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fjall.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Secrets Manager

> Store credentials securely in AWS Secrets Manager with customer-managed KMS encryption using Fjall infrastructure.

## Overview

The Secret resource stores sensitive values such as database passwords, API keys, and other credentials. Each secret is encrypted with a Customer Managed Key (CMK) and accepts simple strings or JSON objects.

## Resource Class

```typescript theme={null}
import { Secret } from "@fjall/components-infrastructure/lib/resources/aws/secrets/secret";
```

## Basic Usage

### Simple Secret

```typescript theme={null}
const secret = new Secret(this, "ApiKey", {
  secretName: "my-api-key",
  secretStringValue: "super-secret-key",
});
```

### Generated Password

```typescript theme={null}
const dbSecret = new Secret(this, "DatabasePassword", {
  secretName: "db-password",
  generateSecretString: {
    secretStringTemplate: JSON.stringify({ username: "admin" }),
    generateStringKey: "password",
    excludePunctuation: true,
    includeSpace: false,
    passwordLength: 32,
  },
});
```

### JSON Secret

```typescript theme={null}
const configSecret = new Secret(this, "AppConfig", {
  secretName: "app-configuration",
  secretObjectValue: {
    apiUrl: SecretValue.unsafePlainText("https://api.example.com"),
    apiKey: SecretValue.unsafePlainText("key-123"),
    environment: SecretValue.unsafePlainText("production"),
  },
});
```

## Configuration Options

### Core Properties

| Property      | Type     | Description        |
| ------------- | -------- | ------------------ |
| `secretName`  | `string` | Name of the secret |
| `description` | `string` | Secret description |
| `aliasName`   | `string` | KMS key alias name |

### Secret Value Options (use one)

| Property               | Type                           | Description                      |
| ---------------------- | ------------------------------ | -------------------------------- |
| `secretStringValue`    | `string`                       | Plain text secret value          |
| `secretObjectValue`    | `{[key: string]: SecretValue}` | JSON object with multiple values |
| `generateSecretString` | `SecretStringGenerator`        | Generate random password         |

## Default Features

Every secret includes:

* **KMS encryption** with Customer Managed Key
* **Automatic key creation** with alias `cmk/${id}`
* **Key retention** on stack deletion (RETAIN policy)
* **Import/export** capabilities

## Generated Passwords

### Basic Password Generation

```typescript theme={null}
const passwordSecret = new Secret(this, "UserPassword", {
  secretName: "user-password",
  generateSecretString: {
    passwordLength: 16,
    excludeCharacters: ' %+~`#$&*()|[]{}:;<>?!\'"/@"\\\\',
  },
});
```

### Database Password with Username

```typescript theme={null}
const dbSecret = new Secret(this, "DBCredentials", {
  secretName: "rds-credentials",
  generateSecretString: {
    secretStringTemplate: JSON.stringify({
      username: "postgres",
      engine: "postgres",
      host: "myapp-db.cluster-abc123.us-east-1.rds.amazonaws.com",
    }),
    generateStringKey: "password",
    excludePunctuation: true,
    passwordLength: 30,
  },
});
```

### API Key Generation

```typescript theme={null}
const apiKeySecret = new Secret(this, "APIKey", {
  secretName: "external-api-key",
  generateSecretString: {
    includeSpace: false,
    passwordLength: 40,
    excludeCharacters: '/@"\\\\ ',
    requireEachIncludedType: false,
  },
});
```

## Import/Export Pattern

### Export for Cross-Stack Usage

```typescript theme={null}
const secret = new Secret(this, "SharedSecret", {
  secretName: "shared-credentials",
});

// Get import configuration
const importConfig = secret.getImport();
// Returns: { id: string, name: string, field?: string }

// Export for other stacks
new CfnOutput(this, "SecretImport", {
  value: JSON.stringify(importConfig),
});
```

### Import Specific Field

```typescript theme={null}
const dbSecret = new Secret(this, "Database", {
  secretName: "db-creds",
  generateSecretString: {
    secretStringTemplate: JSON.stringify({
      username: "admin",
      host: "db.example.com",
    }),
    generateStringKey: "password",
  },
});

// Import just the password field
const passwordImport = dbSecret.getImport("password");
```

## Integration with Resources

### With RDS

```typescript theme={null}
// Used automatically in RDS constructs
const database = new RdsInstance(this, "Database", {
  databaseName: "myapp",
  // Secret created automatically
});

// Access the credentials
const credentials = database.getCredentials();
const passwordImport = credentials.getImport("password");
```

### With ECS

```typescript theme={null}
const appSecret = new Secret(this, "AppSecrets", {
  secretName: "app-secrets",
  secretObjectValue: {
    dbPassword: SecretValue.unsafePlainText("temp"),
    apiKey: SecretValue.unsafePlainText("temp"),
  },
});

const container = taskDefinition.addContainer("app", {
  secrets: {
    DB_PASSWORD: EcsSecret.fromSecretsManager(appSecret, "dbPassword"),
    API_KEY: EcsSecret.fromSecretsManager(appSecret, "apiKey"),
  },
});
```

### With Lambda

```typescript theme={null}
const secret = new Secret(this, "LambdaSecret", {
  secretName: "lambda-config",
});

const fn = new Function(this, "MyFunction", {
  environment: {
    SECRET_ARN: secret.secret.secretArn,
  },
});

// Grant read permission
secret.secret.grantRead(fn);
```

## KMS Encryption

### Default CMK Creation

```typescript theme={null}
const secret = new Secret(this, "MySecret", {
  secretName: "my-secret",
});

// Automatically creates:
// - KMS key with description "${id} KMS Key"
// - Key alias: "cmk/${id}"
// - Retention policy: RETAIN
```

### Access the CMK

```typescript theme={null}
const secret = new Secret(this, "EncryptedSecret", {
  secretName: "encrypted",
});

// Access the key
const kmsKey = secret.secretsCustomerManagedKey.key;
const kmsAlias = secret.secretsCustomerManagedKey.alias;
```

## Secret Rotation

### Enable Rotation

```typescript theme={null}
const dbSecret = new Secret(this, "RotatingSecret", {
  secretName: "db-password",
  generateSecretString: {
    secretStringTemplate: JSON.stringify({ username: "admin" }),
    generateStringKey: "password",
  },
});

// Add rotation (typically done in database constructs)
new SecretRotation(this, "Rotation", {
  application: SecretRotationApplication.POSTGRES_ROTATION,
  secret: dbSecret.secret,
  target: database,
  vpc: vpc,
});
```

## Factory Pattern

### Using StackBuilder

```typescript theme={null}
const secretFactory = Secret.build("ApiSecret", {
  secretName: "api-credentials",
  description: "External API credentials",
});

const secret = secretFactory(stackBuilder);
```

## IAM Permissions

### Grant Read Access

```typescript theme={null}
const secret = new Secret(this, "ReadableSecret", {
  secretName: "app-config",
});

// Grant read to a role
secret.secret.grantRead(lambdaRole);

// Grant read to a service
secret.secret.grantRead(ecsTaskRole);
```

### Custom Permissions

```typescript theme={null}
const secret = new Secret(this, "CustomSecret", {
  secretName: "restricted",
});

role.addToPolicy(
  new PolicyStatement({
    actions: ["secretsmanager:GetSecretValue", "secretsmanager:DescribeSecret"],
    resources: [secret.secret.secretArn],
    conditions: {
      StringEquals: {
        "secretsmanager:VersionStage": "AWSCURRENT",
      },
    },
  }),
);
```

## Outputs

The nested `CustomerManagedKey` construct creates the KMS key outputs. Export
names key off the CMK construct id (`${id}CustomerManagedKey`), run through
`toPascalCase`, so the value below is `name = toPascalCase(`\${id}CustomerManagedKey`)`:

```typescript theme={null}
// Key ARN output
{
  key: `${name}Arn`,
  value: this.key.keyArn,
  exportName: `${name}KeyArn`
}

// Alias ARN output
{
  key: `${name}AliasArn`,
  value: this.alias.aliasArn,
  exportName: `${name}KeyAliasArn`
}
```

## Complete Example

```typescript theme={null}
import { Secret } from "@fjall/components-infrastructure/lib/resources/aws/secrets/secret";
import { SecretValue } from "aws-cdk-lib";

// Application configuration secret
const appConfig = new Secret(this, "AppConfiguration", {
  secretName: `${props.appName}-config-${props.environment}`,
  description: "Application configuration and credentials",
  secretObjectValue: {
    // Database
    dbHost: SecretValue.unsafePlainText(database.getHostEndpoint()),
    dbName: SecretValue.unsafePlainText("production"),

    // External APIs
    stripeKey: SecretValue.unsafePlainText("sk_live_xxx"),
    sendgridKey: SecretValue.unsafePlainText("SG.xxx"),

    // Feature flags
    maintenanceMode: SecretValue.unsafePlainText("false"),
    debugEnabled: SecretValue.unsafePlainText("false"),
  },
});

// Database credentials with rotation
const dbCredentials = new Secret(this, "DatabaseCredentials", {
  secretName: `${props.appName}-db-credentials`,
  description: "RDS master credentials",
  generateSecretString: {
    secretStringTemplate: JSON.stringify({
      username: "dbadmin",
      engine: "postgres",
      host: database.getHostEndpoint(),
      port: 5432,
    }),
    generateStringKey: "password",
    excludePunctuation: true,
    passwordLength: 32,
  },
});

// API key for external service
const apiKey = new Secret(this, "ExternalAPIKey", {
  secretName: "payment-processor-key",
  secretStringValue: props.paymentApiKey, // From environment
  description: "Payment processor API key",
});

// Grant permissions
const appRole = new Role(this, "AppRole", {
  assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"),
});

appConfig.secret.grantRead(appRole);
dbCredentials.secret.grantRead(appRole);
apiKey.secret.grantRead(appRole);

// Use in ECS task
const container = taskDefinition.addContainer("app", {
  environment: {
    APP_NAME: props.appName,
    ENVIRONMENT: props.environment,
  },
  secrets: {
    // From app config
    DB_HOST: EcsSecret.fromSecretsManager(appConfig.secret, "dbHost"),
    DB_NAME: EcsSecret.fromSecretsManager(appConfig.secret, "dbName"),

    // From db credentials
    DB_PASSWORD: EcsSecret.fromSecretsManager(dbCredentials.secret, "password"),

    // From API key
    PAYMENT_API_KEY: EcsSecret.fromSecretsManager(apiKey.secret),
  },
});

// Export for other stacks
new CfnOutput(this, "AppConfigImport", {
  value: JSON.stringify(appConfig.getImport()),
  description: "Import configuration for app secrets",
});
```

## Best Practices

1. **Never hardcode secrets.** Store them in Secrets Manager.
2. **Use generated passwords** for databases.
3. **Rotate secrets** every 30 to 90 days.
4. **Apply least-privilege** IAM policies.
5. **Separate secrets by purpose.** Do not mix unrelated secrets.
6. **Tag secrets** for cost allocation and compliance.
7. **Monitor access** with CloudTrail.

## Cost Optimisation

### Pricing

* **\$0.40** per secret per month
* **\$0.05** per 10,000 API calls

### Cost Saving Tips

1. **Combine related secrets** in JSON objects

```typescript theme={null}
// Instead of multiple secrets
const dbUser = new Secret(this, "DBUser", { secretName: "db-user" });
const dbPass = new Secret(this, "DBPass", { secretName: "db-pass" });
const dbHost = new Secret(this, "DBHost", { secretName: "db-host" });

// Use one secret with JSON
const dbConfig = new Secret(this, "DBConfig", {
  secretName: "db-config",
  secretObjectValue: {
    username: SecretValue.unsafePlainText("admin"),
    password: SecretValue.unsafePlainText("pass"),
    host: SecretValue.unsafePlainText("db.example.com"),
  },
});
```

2. **Cache secrets** in Lambda functions
3. **Use Parameter Store** for non-sensitive config

## Security Considerations

### Secret Value Security

```typescript theme={null}
// Avoid: logs the secret in CloudFormation
const bad = new Secret(this, "Bad", {
  secretStringValue: "my-password", // Visible in templates!
});

// Better: generate the value, or pass it via environment
const good = new Secret(this, "Good", {
  generateSecretString: { passwordLength: 32 },
});

// Best: reference an existing value with SecretValue.ssmSecure
const best = new Secret(this, "Best", {
  secretObjectValue: {
    apiKey: SecretValue.ssmSecure("/myapp/api-key"),
  },
});
```

### Access Patterns

```typescript theme={null}
// Principle of least privilege
const readOnlyPolicy = new PolicyStatement({
  actions: ["secretsmanager:GetSecretValue"],
  resources: [secret.secret.secretArn],
  principals: [readOnlyRole],
});

// Condition-based access
const conditionalPolicy = new PolicyStatement({
  actions: ["secretsmanager:GetSecretValue"],
  resources: [secret.secret.secretArn],
  conditions: {
    IpAddress: {
      "aws:SourceIp": ["10.0.0.0/8"],
    },
  },
});
```

## Troubleshooting

| Symptom           | Cause and fix                                          |
| ----------------- | ------------------------------------------------------ |
| Access denied     | Check IAM permissions and resource policies.           |
| Secret not found  | Verify the secret name and region.                     |
| KMS access error  | Grant the principal key-usage permissions on the CMK.  |
| Rotation failures | Check the rotation Lambda function and network access. |

## Next Steps

<CardGroup cols={2}>
  <Card title="KMS Key" icon="lock" href="/resources/security/kms-key">
    Configure the customer-managed key that encrypts each secret.
  </Card>

  <Card title="IAM Role" icon="user-shield" href="/resources/security/iam-role">
    Grant roles least-privilege read access to secrets.
  </Card>

  <Card title="RDS Instance" icon="database" href="/resources/database/rds-instance">
    Manage database credentials with generated secrets.
  </Card>

  <Card title="ECS Cluster" icon="server" href="/resources/compute/ecs-cluster">
    Inject secrets into container tasks at runtime.
  </Card>
</CardGroup>
