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 Manager | Parameter Store | |
|---|---|---|
| What seekrit writes | One secret per name, or all of them as one JSON secret | SSM parameters directly under one path, SecureString by default |
| Addressed by | An optional name prefix, or the one bundle secret's name | A /-delimited path |
| IAM actions | PutSecretValue, CreateSecret, DeleteSecret, RestoreSecret, ListSecrets | PutParameter, DeleteParameters, GetParametersByPath |
| Takes effect | Next read by your app | Next read by your app |
| Deletion | AWS's scheduled deletion, 30-day recovery window | Immediate |
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.
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.
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 --tier — Standard 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;
PutParameterwithOverwriteis already an upsert. Each secret is written optimistically and only falls back toCreateSecretonResourceNotFoundException— 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,ExpiredTokenand their kin are facts about the connection, not about a name — repeating one beside fifty secrets would bury it.
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
| Symptom | Cause | Fix |
|---|---|---|
AccessDeniedException, whole run stopped | The IAM policy is missing an action, or its Resource doesn't cover the prefix or path the binding writes under | Compare the policy against the actions above. AWS reports this per request, so the run stops and says so once |
UnrecognizedClientException or InvalidSignatureException | The key pair is wrong or was deactivated | Rotating an AWS key changes the id as well as the secret, so a rotation means a new connection, not just a new credential |
ExpiredTokenException | Temporary STS credentials were used | Use a long-lived IAM user key — sync runs unattended |
ThrottlingException, run reported partial | AWS rate-limited the account | Nothing to do — seekrit backs off and retries the rest |
A new secret fails with InvalidRequestException about a scheduled deletion | The name is inside its 30-day recovery window | seekrit restores it automatically on the next run; force one with seekrit sync run |
ParameterLimitExceeded or a value rejected as too long | Standard tier caps a value at 4KB | Pass --tier Advanced (billed) or Intelligent-Tiering |
| Bundle secret missing keys you added by hand | The bundle is rewritten whole every run | Use --layout secret-per-name, or keep hand-managed values in a different secret |
See also
- Third-party sync — the shared model, naming and filtering, deletions
- AWS KMS drop-in — the other direction: KMS-compatible envelope crypto against seekrit
- CLI reference — every
seekrit syncflag