> ## 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.

# KMS Key

> Provision an AWS KMS customer-managed key with Fjall for encrypting databases, buckets, secrets, and logs via CDK.

## Overview

The CustomerManagedKey resource provides AWS KMS encryption keys for protecting sensitive data at rest. It creates an alias automatically and is retained by default to prevent accidental data loss.

## Resource Class

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

## Basic Usage

```typescript theme={null}
const key = new CustomerManagedKey(this, "MyKey", {
  description: "Application encryption key",
  aliasName: "alias/my-app",
});
```

## Configuration Options

| Property        | Type            | Description                           | Default                |
| --------------- | --------------- | ------------------------------------- | ---------------------- |
| `description`   | `string`        | Key description                       | `${id} KMS Key`        |
| `aliasName`     | `string`        | Key alias                             | `cmk/${id}`            |
| `removalPolicy` | `RemovalPolicy` | Behaviour when the stack is torn down | `RemovalPolicy.RETAIN` |

<Warning>
  KMS keys cannot be deleted immediately. Setting `removalPolicy` to `RemovalPolicy.DESTROY` schedules the key for deletion with a 14-day pending window, during which the key is disabled but recoverable. Deleting a KMS key makes every value it encrypted permanently unrecoverable, so keep `RemovalPolicy.RETAIN` for production data.
</Warning>

## Default Features

Every CustomerManagedKey includes:

* **Removal policy**: `RemovalPolicy.RETAIN` (prevents accidental deletion). Set `removalPolicy: RemovalPolicy.DESTROY` to schedule deletion with a 14-day pending window instead.
* **Automatic alias**: Created with the specified or default name
* **CloudFormation outputs**: Both key and alias ARNs exported

## Key Components

### The KMS Key

```typescript theme={null}
const cmk = new CustomerManagedKey(this, "DataKey", {
  description: "Data encryption key",
});

// Access the underlying key
const key = cmk.key;
const keyArn = key.keyArn;
const keyId = key.keyId;
```

### The Key Alias

```typescript theme={null}
const cmk = new CustomerManagedKey(this, "AppKey", {
  aliasName: "alias/myapp/encryption",
});

// Access the alias
const alias = cmk.alias;
const aliasName = alias.aliasName;
const aliasArn = alias.aliasArn;
```

## Common Patterns

### Database Encryption

```typescript theme={null}
// Used automatically in RDS constructs
const database = new RdsAurora(this, "Database", {
  databaseName: "myapp",
  // Creates encryption key automatically
});

// Or explicitly
const dbKey = new CustomerManagedKey(this, "DatabaseKey", {
  description: "RDS encryption key",
  aliasName: "alias/rds/myapp",
});
```

### S3 Bucket Encryption

```typescript theme={null}
const bucketKey = new CustomerManagedKey(this, "BucketKey", {
  description: "S3 bucket encryption key",
  aliasName: "alias/s3/uploads",
});

const bucket = new s3.Bucket(this, "SecureBucket", {
  encryption: s3.BucketEncryption.KMS,
  encryptionKey: bucketKey.key,
  bucketKeyEnabled: true, // Reduce KMS costs
});
```

### Secrets Manager Integration

```typescript theme={null}
// Automatically created with Secret construct
const secret = new Secret(this, "AppSecret", {
  secretName: "app-config",
  // Creates CMK automatically
});

// Access the key
const secretKey = secret.secretsCustomerManagedKey;
```

### EBS Volume Encryption

```typescript theme={null}
const volumeKey = new CustomerManagedKey(this, "VolumeKey", {
  description: "EBS volume encryption",
  aliasName: "alias/ebs/data-volumes",
});

// Grant EC2 service access
volumeKey.key.grantEncryptDecrypt(new ServicePrincipal("ec2.amazonaws.com"));
```

## Key Policies

### Default Key Policy

The default policy allows:

* Root account full access
* Key administrators to manage
* Key users to encrypt/decrypt

### Custom Key Policy

```typescript theme={null}
const customKey = new CustomerManagedKey(this, "CustomKey", {
  description: "Key with custom policy",
});

// Add key administrators
customKey.key.grantAdmin(adminRole);

// Add key users
customKey.key.grantEncryptDecrypt(applicationRole);

// Grant specific permissions
customKey.key.grant(lambdaRole, "kms:Decrypt", "kms:DescribeKey");
```

### Cross-Account Access

```typescript theme={null}
const sharedKey = new CustomerManagedKey(this, "SharedKey", {
  description: "Cross-account shared key",
  aliasName: "alias/shared/data",
});

// Grant access to another account
sharedKey.key.grantEncryptDecrypt(new AccountPrincipal("123456789012"));
```

## Key Rotation

### Enable Automatic Rotation

```typescript theme={null}
const rotatingKey = new CustomerManagedKey(this, "RotatingKey", {
  description: "Auto-rotating key",
});

// Enable rotation (not available through construct, use CDK)
const cfnKey = rotatingKey.key.node.defaultChild as kms.CfnKey;
cfnKey.enableKeyRotation = true;
```

## Removal Policy

Keys are retained when the stack is destroyed by default. Pass `removalPolicy: RemovalPolicy.DESTROY` only for throwaway environments where the encrypted data is disposable.

```typescript theme={null}
import { RemovalPolicy } from "aws-cdk-lib";

const ephemeralKey = new CustomerManagedKey(this, "EphemeralKey", {
  description: "Disposable environment key",
  removalPolicy: RemovalPolicy.DESTROY,
});
```

When `removalPolicy` is `RemovalPolicy.DESTROY`, the construct sets a 14-day pending window. AWS disables the key immediately, then deletes it after the window elapses. You can cancel the deletion during that window to recover the key and any data it protects.

## Outputs

The construct automatically creates these CloudFormation outputs:

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

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

## Grant Permissions

### Encryption/Decryption

```typescript theme={null}
const key = new CustomerManagedKey(this, "AppKey", {
  description: "Application key",
});

// Grant encrypt/decrypt
key.key.grantEncryptDecrypt(lambdaRole);

// Grant decrypt only
key.key.grantDecrypt(readOnlyRole);

// Grant through resource
bucket.grantRead(role); // Includes KMS permissions
```

### Key Management

```typescript theme={null}
const key = new CustomerManagedKey(this, "ManagedKey", {
  description: "Managed key",
});

// Grant admin permissions
key.key.grantAdmin(adminRole);

// Custom permissions
key.key.grant(
  serviceRole,
  "kms:CreateGrant",
  "kms:ListGrants",
  "kms:RevokeGrant",
);
```

## Service Integration

### With RDS

```typescript theme={null}
// Performance Insights
const perfInsightsKey = new CustomerManagedKey(this, "PerfInsightsKey", {
  description: "RDS Performance Insights",
  aliasName: "alias/rds/perf-insights",
});

// Backup encryption
const backupKey = new CustomerManagedKey(this, "BackupKey", {
  description: "RDS backup encryption",
  aliasName: "alias/rds/backups",
});
```

### With Lambda

```typescript theme={null}
const lambdaKey = new CustomerManagedKey(this, "LambdaKey", {
  description: "Lambda environment encryption",
});

const fn = new lambda.Function(this, "SecureFunction", {
  environmentEncryption: lambdaKey.key,
  environment: {
    API_KEY: "encrypted-value",
  },
});
```

### With CloudWatch Logs

```typescript theme={null}
const logKey = new CustomerManagedKey(this, "LogKey", {
  description: "CloudWatch Logs encryption",
  aliasName: "alias/logs/application",
});

const logGroup = new logs.LogGroup(this, "SecureLogs", {
  encryptionKey: logKey.key,
  retention: logs.RetentionDays.ONE_YEAR,
});
```

## Cost Considerations

### Pricing

* **\$1.00** per month per key
* **\$0.03** per 10,000 requests

### Cost Optimisation

1. **Share keys across resources** when appropriate

```typescript theme={null}
// One key for multiple buckets
const sharedKey = new CustomerManagedKey(this, "SharedBucketKey", {
  description: "Shared S3 encryption key",
});

const bucket1 = new s3.Bucket(this, "Bucket1", {
  encryptionKey: sharedKey.key,
});

const bucket2 = new s3.Bucket(this, "Bucket2", {
  encryptionKey: sharedKey.key,
});
```

2. **Use bucket keys** for S3

```typescript theme={null}
const bucket = new s3.Bucket(this, "OptimizedBucket", {
  encryptionKey: key.key,
  bucketKeyEnabled: true, // Reduces KMS calls
});
```

3. **Cache decrypted values** in Lambda

```typescript theme={null}
// Cache outside handler
let decryptedValue: string | undefined;

export const handler = async () => {
  if (!decryptedValue) {
    decryptedValue = await decrypt(process.env.SECRET);
  }
  // Use cached value
};
```

## Complete Example

```typescript theme={null}
import { CustomerManagedKey } from "@fjall/components-infrastructure/lib/resources/aws/secrets/kms";
import { Role, ServicePrincipal } from "aws-cdk-lib/aws-iam";

// Master encryption key for application
const masterKey = new CustomerManagedKey(this, "MasterKey", {
  description: `${props.appName} master encryption key`,
  aliasName: `alias/${props.appName}/master`,
});

// Data encryption key hierarchy
const dataKey = new CustomerManagedKey(this, "DataKey", {
  description: "Data layer encryption",
  aliasName: `alias/${props.appName}/data`,
});

// Secrets encryption
const secretsKey = new CustomerManagedKey(this, "SecretsKey", {
  description: "Secrets Manager encryption",
  aliasName: `alias/${props.appName}/secrets`,
});

// Backup encryption
const backupKey = new CustomerManagedKey(this, "BackupKey", {
  description: "Backup encryption key",
  aliasName: `alias/${props.appName}/backups`,
});

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

// Grant appropriate permissions
dataKey.key.grantEncryptDecrypt(appRole);
secretsKey.key.grantDecrypt(appRole);

// Admin role can manage all keys
const adminRole = new Role(this, "AdminRole", {
  assumedBy: new ServicePrincipal("lambda.amazonaws.com"),
});

[masterKey, dataKey, secretsKey, backupKey].forEach((cmk) => {
  cmk.key.grantAdmin(adminRole);
});

// S3 bucket with encryption
const dataBucket = new s3.Bucket(this, "DataBucket", {
  encryption: s3.BucketEncryption.KMS,
  encryptionKey: dataKey.key,
  bucketKeyEnabled: true,
});

// RDS with encryption
const database = new rds.DatabaseInstance(this, "Database", {
  storageEncryptionKey: dataKey.key,
  performanceInsightEncryptionKey: dataKey.key,
});

// Secrets with dedicated key
const appSecret = new secretsmanager.Secret(this, "AppSecret", {
  encryptionKey: secretsKey.key,
});

// Backup vault with encryption
const vault = new backup.BackupVault(this, "Vault", {
  encryptionKey: backupKey.key,
  backupVaultName: `${props.appName}-vault`,
});

// Tag all keys
[masterKey, dataKey, secretsKey, backupKey].forEach((cmk) => {
  Tags.of(cmk).add("Application", props.appName);
  Tags.of(cmk).add("Environment", props.environment);
  Tags.of(cmk).add("Purpose", "Encryption");
});

// Outputs
new CfnOutput(this, "MasterKeyId", {
  value: masterKey.key.keyId,
  description: "Master KMS key ID",
});
```

## Best Practices

1. **Use aliases** for easier key management
2. **Enable key rotation** for long-lived keys
3. **Separate keys by purpose** (data, secrets, logs)
4. **Grant least privilege** access
5. **Monitor key usage** with CloudTrail
6. **Use key policies** for fine-grained control
7. **Tag keys** for cost allocation

## Security Considerations

### Key Deletion Protection

```typescript theme={null}
// Keys are retained by default
const key = new CustomerManagedKey(this, "ProtectedKey", {
  description: "Cannot be deleted with stack",
});

// Additional protection with key policy
key.key.addToResourcePolicy(
  new PolicyStatement({
    sid: "PreventDeletion",
    principals: [new AnyPrincipal()],
    actions: ["kms:ScheduleKeyDeletion"],
    effect: Effect.DENY,
  }),
);
```

### Compliance

```typescript theme={null}
// HIPAA compliance example
const hipaaKey = new CustomerManagedKey(this, "HIPAAKey", {
  description: "HIPAA compliant encryption key",
  aliasName: "alias/hipaa/phi-data",
});

// Restrict to specific services
hipaaKey.key.addToResourcePolicy(
  new PolicyStatement({
    principals: [new AnyPrincipal()],
    actions: ["kms:*"],
    resources: ["*"],
    conditions: {
      StringEquals: {
        "kms:ViaService": [
          `s3.${this.region}.amazonaws.com`,
          `rds.${this.region}.amazonaws.com`,
        ],
      },
    },
  }),
);
```

## Troubleshooting

### Common Issues

1. **Access denied**: Check key policy and IAM permissions
2. **Key not found**: Verify alias or key ID
3. **Throttling**: Implement exponential backoff
4. **Invalid grant**: Check principal permissions

### Debug Commands

```bash theme={null}
# Describe key
aws kms describe-key --key-id alias/my-app

# List key policies
aws kms list-key-policies --key-id alias/my-app

# Get key policy
aws kms get-key-policy --key-id alias/my-app --policy-name default

# Test encryption
echo "test" | aws kms encrypt --key-id alias/my-app --plaintext fileb://-
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Secrets Manager" icon="key" href="/resources/security/secrets-manager">
    Store and rotate secrets encrypted with a customer-managed key.
  </Card>

  <Card title="S3 Bucket" icon="bucket" href="/resources/storage/s3-bucket">
    Encrypt object storage at rest with your KMS key.
  </Card>

  <Card title="RDS Aurora" icon="database" href="/resources/database/rds-aurora">
    Provision an Aurora cluster with KMS storage and backup encryption.
  </Card>

  <Card title="IAM Role" icon="user-shield" href="/resources/security/iam-role">
    Grant roles least-privilege encrypt and decrypt permissions.
  </Card>
</CardGroup>
