---
title: From AWS Secrets Manager
path: migrations/from-aws-secrets-manager
status: published
---

# Migrate from AWS Secrets Manager

Move from AWS Secrets Manager (ASM) to ScaiVault. Like the HashiCorp Vault migration, the recommended path is incremental: federate first, migrate per-namespace, retire the ASM secrets when nothing reads them.

The structural difference vs Vault: ASM's flat naming (no real hierarchy beyond `/`-separated prefixes) maps cleanly onto ScaiVault's path scheme. The main thing to think about is what to do about ASM's automatic rotation — ScaiVault has its own rotation, so you can either turn ASM's off and use ScaiVault's, or leave ASM rotating and have ScaiVault federate-sync the result.

## What you need

- ScaiVault token with `secrets:write`, `federation:write`, `admin`.
- AWS IAM credentials with `secretsmanager:Get*` and `secretsmanager:ListSecrets` for the relevant secrets. Optionally `secretsmanager:DeleteSecret` for cleanup.
- An ASM region (or several — repeat the setup per region if multi-region).

## 1. Inventory

```bash
aws secretsmanager list-secrets --region us-east-1 \
  --query 'SecretList[*].[Name,Description,Tags[?Key==`environment`].Value | [0]]' \
  --output table
```

Group by purpose:

- Per-service credentials (typical: `prod/billing/stripe`, `prod/billing/database`).
- ASM-rotated database passwords (managed by ASM, rotated via Lambda).
- Encryption material referenced by KMS aliases.

## 2. Set up IAM for ScaiVault

A minimum policy:

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue",
        "secretsmanager:DescribeSecret",
        "secretsmanager:ListSecrets",
        "secretsmanager:ListSecretVersionIds"
      ],
      "Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/*"
    }
  ]
}
```

Attach to an IAM user or role that ScaiVault will use. Note the ARN pattern restricts to the `prod/` prefix; tighten further as you migrate per-team.

## 3. Store the AWS credentials in ScaiVault

```bash
curl -X PUT https://scaivault.scailabs.ai/v1/secrets/infra/aws/sm-reader \
  -H "Authorization: Bearer $SCAIVAULT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "data": {"access_key_id": "AKIA...", "secret_access_key": "..."},
    "secret_type": "json"
  }'
```

Or, if ScaiVault is running in AWS with an IAM role, configure the federation backend to use that role directly — no static creds needed.

## 4. Configure federation

```bash
curl -X POST https://scaivault.scailabs.ai/v1/federation/backends \
  -H "Authorization: Bearer $SCAIVAULT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "aws-prod-sm",
    "type": "aws-secrets-manager",
    "config": {
      "region": "us-east-1",
      "credentials_path": "infra/aws/sm-reader"
    },
    "path_mapping": {
      "external/aws/**": "prod/**"
    },
    "mode": "sync",
    "sync_interval": "15m"
  }'
```

**Why `sync` and not `proxy`?** ASM charges per API call (~$0.05 per 10,000 reads). For a hot path, sync into local storage is much cheaper. Set `sync_interval` to whatever staleness your application tolerates — most services can live with 15 minutes.

For low-volume sensitive secrets where freshness matters more than cost, use `proxy` instead.

Verify a read:

```bash
# ASM secret "prod/billing/stripe" -> ScaiVault path "external/aws/billing/stripe"
curl -H "Authorization: Bearer $SCAIVAULT_TOKEN" \
     https://scaivault.scailabs.ai/v1/secrets/external/aws/billing/stripe
```

Should return whatever ASM has at `prod/billing/stripe`.

## 5. Migrate one secret to the new path scheme

Federation gives you read access through ScaiVault, but the path is awkward (`external/aws/...`). Move to a cleaner scheme that doesn't telegraph the origin.

```bash
# Read from ASM via federation
value=$(curl -s -H "Authorization: Bearer $SCAIVAULT_TOKEN" \
  https://scaivault.scailabs.ai/v1/secrets/external/aws/billing/stripe | jq -c .data)

# Write to the new canonical path
curl -X PUT https://scaivault.scailabs.ai/v1/secrets/environments/production/billing/stripe \
  -H "Authorization: Bearer $SCAIVAULT_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"data\": $value,
    \"secret_type\": \"json\",
    \"metadata\": {
      \"tags\": [\"billing\", \"stripe\", \"migrated-from-asm\"],
      \"owner\": \"team:billing\"
    },
    \"options\": {\"max_versions\": 10}
  }"
```

Script the whole batch:

```python
import httpx, os

SV  = "https://scaivault.scailabs.ai"
SVT = os.environ["SCAIVAULT_TOKEN"]
H   = {"Authorization": f"Bearer {SVT}"}

MAPPING = {
    "external/aws/billing/stripe":     "environments/production/billing/stripe",
    "external/aws/billing/database":   "environments/production/billing/database",
    "external/aws/reporting/salesforce": "shared/integrations/salesforce/oauth",
}

for src, dst in MAPPING.items():
    value = httpx.get(f"{SV}/v1/secrets/{src}", headers=H).json()
    httpx.put(
        f"{SV}/v1/secrets/{dst}",
        headers=H,
        json={
            "data": value["data"],
            "secret_type": value.get("secret_type", "json"),
            "metadata": {"tags": ["migrated-from-asm"]},
        },
    ).raise_for_status()
    print(f"  {src} -> {dst}")
```

## 6. Repoint consumers

Same pattern as the [.env migration tutorial](../tutorials/migrate-from-env-files): config flag, staging rollout, production rollout with fallback, remove fallback.

For most apps using the AWS SDK directly, the change is replacing:

```python
import boto3
client = boto3.client("secretsmanager")
secret = json.loads(client.get_secret_value(SecretId="prod/billing/stripe")["SecretString"])
```

with:

```python
from scaivault_sdk import SyncScaiVaultClient
client = SyncScaiVaultClient(base_url=..., token=...)
secret = client.secrets.read("environments/production/billing/stripe").data
```

The path is now product-facing, not AWS-account-specific.

## 7. Handle ASM-rotated secrets

ASM secrets with rotation enabled have a Lambda function that owns the rotation. Two paths:

**Option A — turn off ASM rotation, use ScaiVault's.**

1. Migrate the secret to ScaiVault.
2. In AWS Console (or via API): disable rotation on the ASM secret.
3. Create a ScaiVault rotation policy and assign the migrated secret.
4. If the secret needs the rotation to happen at the source system (e.g. RDS password), wire a webhook on `rotation.due` that re-runs the equivalent logic against the source.

**Option B — keep ASM rotation, ScaiVault syncs the result.**

1. Migrate to ScaiVault. Keep ASM as the authority.
2. Configure federation in `sync` mode — every 15min ScaiVault picks up the latest version from ASM.
3. Consumers read from ScaiVault. When ASM rotates, the new value propagates to ScaiVault on the next sync cycle.
4. Bounded staleness: up to `sync_interval`. For most apps with retries, this is fine.

Option A is the long-term path. Option B is the right transitional choice if you don't want to rebuild the rotation Lambda logic right now.

## 8. Clean up

After consumers read from the new paths and audit shows zero reads from federated `external/aws/...` paths:

```bash
# In AWS
aws secretsmanager delete-secret --secret-id prod/billing/stripe \
  --recovery-window-in-days 30 --region us-east-1

# When you're sure, run again with --force-delete-without-recovery
```

If you keep federation in place (Option B), don't delete the ASM secret — ScaiVault still depends on it.

## ASM-specific considerations

**Versioning.** ASM has version IDs (UUIDs); ScaiVault has integer versions. The federation sync maps `AWSCURRENT` to the current ScaiVault version. ASM versions you care about preserving need to be exported separately — ScaiVault won't recreate the full ASM history.

**Tags.** ASM tags don't map cleanly to ScaiVault `metadata.tags` (ASM tags are key/value; ScaiVault tags are bare strings). The federation backend flattens ASM tags into ScaiVault tag strings of the form `key=value` by default. Customize via `path_mapping`'s `transform` option if you need a different scheme.

**Lambda rotation.** Disable in ASM **before** turning off the consumers. Otherwise the Lambda rotates the underlying DB password while consumers are still reading from ASM with cached creds. Order of operations: disable rotation → migrate → switch consumers → turn off ASM secret.

**Cross-region replication.** If your ASM secrets are replicated across regions, configure a ScaiVault federation backend per region; same source mapping, different `config.region`.

## Common questions

**"ASM is much cheaper than ScaiVault for our use case."** ScaiVault doesn't charge per-API-call, so high-read-volume workloads usually come out ahead. But run the numbers on your specific traffic shape before committing.

**"We use ASM with KMS customer-managed keys."** Those keys stay in KMS; ScaiVault doesn't move them. If you want to migrate the KMS configuration itself, that's a separate workstream — your AWS KMS keys are still your AWS KMS keys.

**"Can ASM read from ScaiVault instead of the reverse?"** Not directly. ASM doesn't have a federation feature. You'd need a Lambda that polls ScaiVault and writes back to ASM — possible but rarely worth the effort. Better to move consumers off ASM.

## What's next

- [Federation Reference](../reference/federation) — full backend config schema.
- [Federation Deep Dive](../advanced/federation) — modes and conflicts.
- [Rotation tutorial](../tutorials/rotate-oauth-credentials) — the ScaiVault-driven rotation pattern.
