# Snowflake: AWS Secret Manager Password Rotation

You can use [AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html) to automatically rotate of passwords using a custom lambda function. AWS provides lambda function templates for many [AWS services](https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotate-secrets_turn-on-for-db.html) (RDS, DocumentDB, etc.) and a [template](https://docs.aws.amazon.com/secretsmanager/latest/userguide/reference_available-rotation-templates.html) you can use to develop your own functions.

Secrets Manager uses a labeling scheme during password rotation.

1. Ensure there is a new secret, labeled AWSPENDING. Create if needed.
    
2. Connect using the current secret, labeled AWSCURRENT, and change the password
    
3. Test connection using the AWSPENDING secret
    
    1. if successful, AWSCURRENT -&gt; AWSPREVIOUS, AWSPENDING -&gt; AWSCURRENT
        
    2. if failed, leave everything as is
        

The idea is to try and ensure that there's no way a failure that can result in your credentials getting lost, and that the process should automatically recover.

## Modifying the MySQL RDS Procedure

I started with the AWS-provided [RDS MySQL](https://github.com/aws-samples/aws-secrets-manager-rotation-lambdas/blob/master/SecretsManagerRDSMySQLRotationSingleUser/lambda_function.py) function. Then I made the changes necessary to get it working with Snowflake.

High-Level Overview of Steps:

* Replaced pymsql with snowflake.connector
    
* Restructured secret format to match Snowflake connection parameters
    
* Removed MySQL SSL-handling code
    
* Updated password change code to Snowflake syntax
    
* Updated connection test code to Snowflake syntax
    

The exact changes are easily visible in the [commit history](https://gitlab.com/nrawling/custom-aws-secrets-manager-rotation-lambdas/-/commit/edd444455b211e374209cb3aa8ae8e08db001101).

## Permissions

The Lambda needs some permissions to rotate the secret:

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:us-east-1:121769289400:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:us-east-1:121769289400:log-group:/aws/lambda/SecretsManagerSnowflakeRotationSingleUser:*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "secretsmanager:DescribeSecret",
                "secretsmanager:GetSecretValue",
                "secretsmanager:PutSecretValue",
                "secretsmanager:UpdateSecretVersionStage"
            ],
            "Resource": "arn:aws:secretsmanager:us-east-1:121769289400:secret:snowflake-ofxx6S"
        },
        {
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetRandomPassword"
            ],
            "Resource": "*"
        },
        {
            "Action": [
                "ec2:CreateNetworkInterface",
                "ec2:DeleteNetworkInterface",
                "ec2:DescribeNetworkInterfaces",
                "ec2:DetachNetworkInterface"
            ],
            "Resource": "*",
            "Effect": "Allow"
        }
    ]
}
```

The lambda needs to be able to:

* Write logs to CloudWatch
    
* Create new random secrets, and administer the secret
    
* Access the KMS key the secret is encrypted with (by default, the service key has access)
    
* (optional) Connect to a VPC
    

Secrets Manager needs to have access to invoke the lambda, as well:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1725753623386/cefe4fcb-7401-44fe-9f70-6622d6f85d8b.png align="center")

## Testing Rotation

You can see it running the [four stages](https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotate-secrets_lambda-functions.html#rotate-secrets_lambda-functions-code) in CloudWatch during the scheduled rotation:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1725754059517/f42a46ce-4a45-442a-a37a-f09d2cdfdd40.png align="center")
