AWS Secrets Manager Managed External Secrets: Automatic Credential Rotation for Snowflake
AWS now handles Snowflake credential rotation automatically via key-pair auth or Programmatic Access Tokens. No Lambda functions, no manual scripts—just configure and forget.
Credential rotation is one of those tasks that everyone knows is important and almost no one does properly.
The typical pattern: Create a Snowflake service account. Store the password in Secrets Manager. Write a Lambda function to rotate it. Forget about the Lambda. It breaks. Credentials expire. Pipeline fails at 3 AM.
AWS eliminated most of that pain with Managed External Secrets, which provides automatic rotation for third-party SaaS credentials — including Snowflake — without custom Lambda functions.
Features change from time to time with new features being added regularly, it is recommended that you review the documentation ↗ for the latest on what specific features are included with any of the Editions.
What Changed
Before, you had to:
- Write a Lambda function that authenticates to Snowflake and rotates the credential
- Handle safe rotation without locking yourself out mid-rotation
- Manage Lambda permissions, VPC configuration, and error handling
- Test it. Maintain it. Debug it when it inevitably breaks.
Now:
- Create the secret in the format Snowflake’s integration expects
- Point Secrets Manager at an IAM role scoped to that secret type
- Enable automatic rotation
- Done — no Lambda, no custom rotation code, no maintenance burden
Launch Partners
At launch (November 2025), managed external secrets supported three partners: Snowflake, Salesforce, and BigID. The partner list has grown steadily since — as of mid-2026 it also includes Confluent Cloud, Datadog, GitLab, MongoDB Atlas, and Paddle, with a May 2026 update specifically adding Snowflake Programmatic Access Token (PAT) rotation alongside the original key-pair support.
How Rotation Actually Works for Snowflake
This is the part worth getting right, because it’s easy to assume Snowflake rotation works the same way as AWS’s classic RDS-style Lambda rotation — a superuser account resets another user’s password. It doesn’t. Snowflake supports two managed external secret types, and neither one is password-based:
| Secret type | Auth mechanism | What gets rotated |
|---|---|---|
SnowflakeKeyPairAuthentication | RSA key-pair | The key pair itself |
SnowflakePat | Key-pair (to authenticate) + Programmatic Access Token (the credential your app actually uses) | The PAT value, with a configurable grace period |
There is no masterarn, no separate “rotation admin” Snowflake user, and no ALTER USER ... SET PASSWORD. Instead,
Secrets Manager rotates the secret by calling Snowflake directly using an IAM role you grant it — the same mechanism
the service uses for every managed external secrets partner.
Option 1: Key-Pair Authentication (SnowflakeKeyPairAuthentication)
The secret value must contain exactly these fields:
aws secretsmanager create-secret \
--name prod/snowflake/etl_service_account \
--description "Snowflake ETL service account — key-pair auth" \
--type SnowflakeKeyPairAuthentication \
--secret-string '{
"account": "myorg-myaccount",
"user": "ETL_SERVICE_ACCOUNT",
"privateKey": "<your-rsa-private-key-pem>",
"publicKey": "<your-rsa-public-key-pem>",
"passphrase": ""
}'bashThe Snowflake user must already be configured for key-pair authentication with the matching public key assigned to its profile before you create the secret — Secrets Manager rotates the key pair going forward, it doesn’t perform the initial setup.
Optional rotation metadata controls how the new key pair is generated:
aws secretsmanager rotate-secret \
--secret-id prod/snowflake/etl_service_account \
--external-secret-rotation-role-arn arn:aws:iam::123456789012:role/snowflake-secret-rotation \
--rotation-rules '{"AutomaticallyAfterDays": 30}' \
--external-secret-rotation-metadata '[
{"Key": "cryptographicAlgorithm", "Value": "RS256"},
{"Key": "encryptPrivateKey", "Value": "false"}
]'bashBoth metadata fields are optional — cryptographicAlgorithm defaults to RS256 (also accepts RS384/RS512), and
encryptPrivateKey defaults to false.
Option 2: Programmatic Access Tokens (SnowflakePat, added May 2026)
If you’re already standardising on PATs rather than key-pair sessions, this is the newer, arguably more natural fit. The secret carries both the key-pair credentials (used only to authenticate the rotation call) and the PAT value itself:
aws secretsmanager create-secret \
--name prod/snowflake/etl_pat \
--description "Snowflake ETL service account — PAT" \
--type SnowflakePat \
--secret-string '{
"account": "myorg-myaccount",
"user": "ETL_SERVICE_ACCOUNT",
"privateKey": "<your-rsa-private-key-pem>",
"passphrase": "",
"patTokenName": "ETL_PIPELINE_TOKEN",
"patTokenValue": "current-pat-value-here"
}'bashDuring rotation, AWS connects using the (unrotated) key pair and runs Snowflake’s ALTER USER ... ROTATE PAT command,
which atomically issues a new token and expires the old one after a configurable grace period — so in-flight connections
using the previous token keep working until it actually expires:
aws secretsmanager rotate-secret \
--secret-id prod/snowflake/etl_pat \
--external-secret-rotation-role-arn arn:aws:iam::123456789012:role/snowflake-secret-rotation \
--rotation-rules '{"AutomaticallyAfterDays": 14}' \
--external-secret-rotation-metadata '[
{"Key": "daysToExpiry", "Value": "15"},
{"Key": "expireOldTokenAfterHours", "Value": "24"}
]'bashdaysToExpiry must match the DAYS_TO_EXPIRY value the token was created with in Snowflake (1–365, default 15) —
Secrets Manager uses it to sanity-check that your rotation schedule is shorter than the token’s actual lifetime.
expireOldTokenAfterHours (0–720, default 24) is the grace period; set it to 0 for immediate expiry of the old token
instead of an overlap window.
The IAM Role Secrets Manager Rotates With
Unlike the classic Lambda pattern, there’s no rotation function to write — but Secrets Manager still needs an IAM role it can assume to call its own APIs on the secret’s behalf, scoped to the specific secret type:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowRotationAccess",
"Effect": "Allow",
"Action": [
"secretsmanager:DescribeSecret",
"secretsmanager:GetSecretValue",
"secretsmanager:PutSecretValue",
"secretsmanager:UpdateSecretVersionStage"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"secretsmanager:resource/Type": "SnowflakeKeyPairAuthentication"
}
}
}
]
}jsonTrust policy — this only permits the Secrets Manager service to assume the role, scoped to your account:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SecretsManagerPrincipalAccess",
"Effect": "Allow",
"Principal": { "Service": "secretsmanager.amazonaws.com" },
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": { "aws:SourceAccount": "123456789012" },
"ArnLike": { "aws:SourceArn": "arn:aws:secretsmanager:eu-west-1:123456789012:secret:*" }
}
}
]
}jsonThe console’s “Create a new role” option during secret setup generates an equivalent policy for you, scoped automatically to the partner and region.
Terraform Configuration
The AWS provider supports managed external secrets, but check your provider version — at time of writing, the documented
type values on aws_secretsmanager_secret cover SnowflakeKeyPairAuthentication (alongside SalesforceClientSecret
and BigIDClientSecret); if you need SnowflakePat via Terraform specifically, verify it’s landed in your provider’s
changelog before relying on it — it may still require console or CLI setup.
# secrets.tf
resource "aws_secretsmanager_secret" "snowflake_etl" {
name = "prod/snowflake/etl_service_account"
description = "Snowflake ETL service account — managed key-pair rotation"
type = "SnowflakeKeyPairAuthentication"
tags = {
Environment = "production"
Application = "data-pipeline"
}
}
resource "aws_secretsmanager_secret_version" "snowflake_etl" {
secret_id = aws_secretsmanager_secret.snowflake_etl.id
secret_string = jsonencode({
account = var.snowflake_account
user = "ETL_SERVICE_ACCOUNT"
privateKey = var.initial_private_key # Rotated automatically going forward
publicKey = var.initial_public_key
passphrase = ""
})
}
resource "aws_secretsmanager_secret_rotation" "snowflake_etl" {
secret_id = aws_secretsmanager_secret.snowflake_etl.id
external_secret_rotation_role_arn = aws_iam_role.snowflake_secret_rotation.arn
rotation_rules {
automatically_after_days = 30
}
# No rotation_lambda_arn — this is what makes it "managed"
}
resource "aws_iam_role" "snowflake_secret_rotation" {
name = "snowflake-secret-rotation"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Service = "secretsmanager.amazonaws.com" }
Action = "sts:AssumeRole"
Condition = {
StringEquals = { "aws:SourceAccount" = data.aws_caller_identity.current.account_id }
}
}]
})
}hclConsuming Rotated Secrets in Applications
Key-pair authentication changes how your application connects — the Snowflake connector takes a private key, not a password.
Python (boto3 + snowflake-connector-python)
import json
import boto3
from cryptography.hazmat.primitives import serialization
from snowflake.connector import connect
def get_snowflake_connection():
"""Get a Snowflake connection using key-pair credentials rotated by Secrets Manager."""
client = boto3.client('secretsmanager', region_name='eu-west-1')
response = client.get_secret_value(
SecretId='prod/snowflake/etl_service_account'
)
secret = json.loads(response['SecretString'])
private_key = serialization.load_pem_private_key(
secret['privateKey'].encode(),
password=secret['passphrase'].encode() if secret['passphrase'] else None,
)
private_key_bytes = private_key.private_bytes(
encoding=serialization.Encoding.DER,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption(),
)
return connect(
account=secret['account'],
user=secret['user'],
private_key=private_key_bytes,
)
# Usage
with get_snowflake_connection() as conn:
cursor = conn.cursor()
cursor.execute("SELECT CURRENT_USER(), CURRENT_ROLE()")
print(cursor.fetchone())pythonPAT Variant
If you’re using the SnowflakePat secret type, your application connects with the token as a password rather than
loading a private key — the private key in that secret is only there for AWS to run the rotation itself:
def get_snowflake_connection_pat():
client = boto3.client('secretsmanager', region_name='eu-west-1')
response = client.get_secret_value(SecretId='prod/snowflake/etl_pat')
secret = json.loads(response['SecretString'])
return connect(
account=secret['account'],
user=secret['user'],
password=secret['patTokenValue'],
)pythonAWS Lambda with Caching
For Lambda functions, use the Secrets Manager caching library to reduce API calls:
from aws_secretsmanager_caching import SecretCache, SecretCacheConfig
import json
cache_config = SecretCacheConfig(secret_refresh_interval=300) # 5 minutes
cache = SecretCache(config=cache_config)
def handler(event, context):
secret_string = cache.get_secret_string('prod/snowflake/etl_pat')
secret = json.loads(secret_string)
with connect(
account=secret['account'],
user=secret['user'],
password=secret['patTokenValue'],
) as conn:
# Your ETL logic here
passpythonBest Practices
1. Use Least-Privilege for Service Accounts
The rotation mechanism doesn’t grant Snowflake permissions — that’s still entirely your responsibility via ordinary Snowflake RBAC:
-- Create a role with only necessary permissions
CREATE ROLE etl_reader;
GRANT USAGE ON WAREHOUSE etl_wh TO ROLE etl_reader;
GRANT USAGE ON DATABASE raw TO ROLE etl_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA raw.public TO ROLE etl_reader;
-- Assign to service account
CREATE USER etl_service_account
DEFAULT_ROLE = etl_reader
DEFAULT_WAREHOUSE = etl_wh;
GRANT ROLE etl_reader TO USER etl_service_account;sql2. Monitor Rotation Events
Set up CloudWatch alarms for rotation failures:
aws cloudwatch put-metric-alarm \
--alarm-name "SnowflakeSecretRotationFailed" \
--metric-name "RotationFailed" \
--namespace "AWS/SecretsManager" \
--statistic Sum \
--period 300 \
--threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold \
--evaluation-periods 1 \
--alarm-actions arn:aws:sns:eu-west-1:123456789012:alertsbash3. Restrict Network Access to the Managed Prefix List
If your Snowflake network policy restricts inbound IPs, allow the AWS-managed prefix list for managed external secrets rather than hardcoding IP ranges — it updates automatically as AWS’s IPs change:
com.amazonaws.{region}.secretsmanager-managed-external-secretsplaintext4. Test Rotation Before Production
# Trigger manual rotation
aws secretsmanager rotate-secret \
--secret-id prod/snowflake/etl_service_account \
--rotate-immediately
# Verify the rotated secret
aws secretsmanager get-secret-value \
--secret-id prod/snowflake/etl_service_account \
--query SecretString --output text | jq .bashCommon Pitfalls
Pitfall 1: Hardcoded Credentials in Legacy Code
Problem: Old scripts still use hardcoded passwords or static key files. Rotation breaks them the moment the secret changes.
Solution: Audit all Snowflake connections. Migrate to Secrets Manager retrieval before enabling rotation.
# Find hardcoded Snowflake credentials (crude but effective)
grep -rE "snowflake" --include="*.py" --include="*.yaml" | grep -iE "password|private_key"bashPitfall 2: Assuming Password Rotation Still Applies
Problem: Reaching for the classic Lambda-based rotation pattern — a masterarn and a dedicated admin user — for a
partner-managed secret type. Managed external secrets for Snowflake don’t use passwords at all; there’s no admin account
to create or lock yourself out of.
Solution: Use the IAM role-based flow described above. If your organisation still needs password-based Snowflake auth for some other reason, that falls outside managed external secrets and needs the classic custom-Lambda approach.
Pitfall 3: Application Caches Stale Credentials
Problem: Application caches credentials at startup. After rotation, it keeps using the old key or token until restart.
Solution: Implement credential refresh logic. On authentication failure, fetch fresh credentials from Secrets
Manager and retry — and for PAT rotation, lean on the grace period (expireOldTokenAfterHours) so a brief cache
staleness doesn’t cause a hard failure.
Conclusion
Managed External Secrets removes the operational burden of credential rotation for Snowflake — but it does so via key-pair authentication and Programmatic Access Tokens, not the password-rotation pattern most AWS documentation examples are built around. For Snowflake users on AWS, this means:
- No custom Lambda functions to write or maintain
- Automatic, scheduled rotation of key pairs or PATs, with a configurable grace period for PATs
- Consistent security posture across all SaaS credentials, without ever sharing Snowflake admin credentials with AWS
The hard part isn’t the AWS configuration — it’s migrating applications from password-based connections to key-pair or PAT-based ones. Do that migration first, then enable rotation.
Key Takeaways:
- AWS Managed External Secrets handles Snowflake credential rotation automatically via key-pair auth
(
SnowflakeKeyPairAuthentication) or Programmatic Access Tokens (SnowflakePat, added May 2026) — not passwords - No Lambda functions required — rotation runs through an IAM role you grant, not a
masterarnor admin account - PAT rotation supports a configurable grace period for zero-downtime token transitions
- Migrate applications to key-pair/PAT auth before enabling rotation
- Monitor rotation events via CloudWatch and restrict network access via the managed prefix list
Features change from time to time with new features being added regularly, it is recommended that you review the documentation ↗ for the latest on what specific features are included with any of the Editions.