seekrit
Docs/AWS

Sync to AWS

The AWS destinations are usually chosen for a different reason than the others. You often can decrypt on your own side in AWS — seekrit run in an ECS container, an SDK in a Lambda — and where you can, you should. Sync to Secrets Manager or Parameter Store when something else already reads from them: an ECS task definition's secrets: block, a Terraform data source, a CloudFormation {{resolve:ssm-secure:…}}, or a team convention you are not going to change.

Secrets ManagerParameter Store
What seekrit writesOne secret per name, or all of them as one JSON secretSSM parameters directly under one path, SecureString by default
Addressed byAn optional name prefix, or the one bundle secret's nameA /-delimited path
IAM actionsPutSecretValue, CreateSecret, DeleteSecret, RestoreSecret, ListSecretsPutParameter, DeleteParameters, GetParametersByPath
Takes effectNext read by your appNext read by your app
DeletionAWS's scheduled deletion, 30-day recovery windowImmediate

New to sync? Read Third-party sync first — the decryption grant, name mapping, deletions, and failure handling are the same on every destination.

The connection: a region and one IAM key

Both AWS destinations share one connection shape: a region, an IAM access key ID, and the secret access key that pairs with it.

Only the secret access key is treated as a credential. An access key ID is an identifier — it appears in CloudTrail, in the IAM console, and in the Authorization header of every signed request — so seekrit stores it in the clear, where the dashboard can show you which key a connection is using. That is the first thing you want to know when a connection starts failing after a key rotation. The secret half is encrypted in your browser, like every other destination credential.

note

Use a long-lived key belonging to an IAM user created for this. Temporary ASIA… credentials from STS expire within hours, and a sync connection has to keep working unattended.

1. Create an IAM user with one policy

Give it only what its destination needs. For Secrets Manager, scoped to the prefix the binding writes under:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "secretsmanager:PutSecretValue",
        "secretsmanager:CreateSecret",
        "secretsmanager:DeleteSecret",
        "secretsmanager:RestoreSecret"
      ],
      "Resource": "arn:aws:secretsmanager:us-east-1:111122223333:secret:prod/storefront/*"
    },
    { "Effect": "Allow", "Action": "secretsmanager:ListSecrets", "Resource": "*" }
  ]
}

ListSecrets cannot be resource-scoped and is only used by Test connection; drop it if you would rather find out at the first push. A customer-managed --kms-key-id additionally needs kms:GenerateDataKey and kms:Decrypt on that key.

For Parameter Store, scoped to the path:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["ssm:PutParameter", "ssm:DeleteParameters", "ssm:GetParametersByPath"],
      "Resource": "arn:aws:ssm:us-east-1:111122223333:parameter/prod/storefront/*"
    },
    { "Effect": "Allow", "Action": "kms:Encrypt", "Resource": "arn:aws:kms:…:key/…" }
  ]
}

SecureString parameters are encrypted with KMS, so the key needs kms:Encrypt — the account's aws/ssm default key, or whichever one the binding names.

2. Add the connection

printf '%s' "$AWS_SECRET_ACCESS_KEY" | seekrit sync connect \
  --name acme-aws --provider aws-secrets-manager \
  --region us-east-1 --access-key-id AKIAIOSFODNN7EXAMPLE

One connection covers one region. Syncing the same environment into two regions means two connections — which also keeps their key grants separate.

There is no account id to supply: every endpoint is reached through the regional host and authorizes off the signature, so the account is whichever one the key belongs to.

3. Bind an environment

Secrets Manager

Choose how the secrets are laid out. The default writes one AWS secret per seekrit secret, under an optional prefix:

seekrit sync verify acme-aws --provider aws-secrets-manager --path prod/storefront/

seekrit sync enable --connection acme-aws \
  --provider aws-secrets-manager --path prod/storefront/ \
  --app storefront --env production --acknowledge-decryption

The alternative packs every value into one secret as a JSON object — the shape an ECS task definition or Lambda reads with secret-arn:json-key:::

seekrit sync enable --connection acme-aws \
  --provider aws-secrets-manager --layout json-bundle \
  --secret-name prod/storefront/env \
  --app storefront --env production --acknowledge-decryption

Prefer the bundle when your runtime reads secrets as a group — Secrets Manager bills per secret per month, so fifty names cost fifty times as much stored separately. Prefer one-per-name when consumers read them individually or you want per-secret IAM.

note

seekrit rewrites the bundle whole on every run, so it mirrors exactly what the binding resolves: a removed secret disappears from it, and so would a key you added by hand. onDelete: retain cannot apply to a single JSON value — if you need removals left in place, use the one-secret-per-name layout.

Parameter Store

Name the path every parameter lands directly under, so an application can read the whole environment with one GetParametersByPath:

seekrit sync enable --connection acme-ssm \
  --provider aws-parameter-store --path /prod/storefront/ \
  --app storefront --env production --acknowledge-decryption

The path needs a leading and trailing slash, so the binding's names concatenate unambiguously — /prod/storefront/ + DB_URL. AWS reserves aws and ssm as a first segment.

Values are written as SecureString unless you pass --param-type String, and on the free Standard tier unless you pass --tierStandard caps a value at 4KB, Advanced at 8KB and bills per parameter per month, and Intelligent-Tiering lets AWS upgrade only the parameters that need it.

How a push behaves

  • Neither connector lists before writing. Secrets Manager has no bulk write, so a listing would add requests without removing any; PutParameter with Overwrite is already an upsert. Each secret is written optimistically and only falls back to CreateSecret on ResourceNotFoundException — one request per secret in the steady state, two the first time.
  • Parameter Store batches deletions ten at a time (DeleteParameters), the one place this API is kinder than Secrets Manager's.
  • Secrets seekrit creates carry a description marking them as managed, so the console says who owns them. A secret that already existed keeps its own.
  • A run is capped at 400 requests, and reports more work rather than silently syncing a subset.
  • A credential or permission failure stops the run once. AccessDenied, UnrecognizedClientException, InvalidSignatureException, ExpiredToken and their kin are facts about the connection, not about a name — repeating one beside fifty secrets would bury it.
note

Deletion on Secrets Manager is scheduled, not immediate. seekrit takes AWS's 30-day default recovery window, so a mistaken removal is recoverable in the console. The consequence is that the name stays reserved for those 30 days and CreateSecret on it fails — so a secret that is deleted and then comes back (renamed away and back, an exclude glob edited twice) is restored rather than recreated.

Troubleshooting

SymptomCauseFix
AccessDeniedException, whole run stoppedThe IAM policy is missing an action, or its Resource doesn't cover the prefix or path the binding writes underCompare the policy against the actions above. AWS reports this per request, so the run stops and says so once
UnrecognizedClientException or InvalidSignatureExceptionThe key pair is wrong or was deactivatedRotating an AWS key changes the id as well as the secret, so a rotation means a new connection, not just a new credential
ExpiredTokenExceptionTemporary STS credentials were usedUse a long-lived IAM user key — sync runs unattended
ThrottlingException, run reported partialAWS rate-limited the accountNothing to do — seekrit backs off and retries the rest
A new secret fails with InvalidRequestException about a scheduled deletionThe name is inside its 30-day recovery windowseekrit restores it automatically on the next run; force one with seekrit sync run
ParameterLimitExceeded or a value rejected as too longStandard tier caps a value at 4KBPass --tier Advanced (billed) or Intelligent-Tiering
Bundle secret missing keys you added by handThe bundle is rewritten whole every runUse --layout secret-per-name, or keep hand-managed values in a different secret

See also