Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New pattern: eventbridge-schedule-secret-rotation-cdk #2469

Merged
merged 6 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions eventbridge-schedule-secret-rotation-cdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Amazon EventBridge Scheduler to AWS Lambda to AWS Secrets Manager

This sample project demonstrates rotating secrets in AWS Secrets Manager using Amazon EventBridge Scheduler and AWS Lambda at desired intervals.

Learn more about this pattern at Serverless Land Patterns: [https://serverlessland.com/patterns/eventbridge-schedule-secret-rotation-cdk](https://serverlessland.com/patterns/eventbridge-schedule-secret-rotation-cdk)

Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example.

## Requirements

* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
* [AWS CDK Toolkit](https://docs.aws.amazon.com/cdk/v2/guide/cli.html) (AWS CDK Toolkit) installed and configured

senthilkumarmohan marked this conversation as resolved.
Show resolved Hide resolved
## Deployment Instructions

1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository:
```
git clone https://github.com/aws-samples/serverless-patterns
```

2. Create and activate Python virtual environment. For instructions, please refer this [page](https://python.land/virtual-environments/virtualenv#Python_venv_activation)

3. Install the required pacakages
```
pip install -r requirements.txt
```

4. Change directory to the pattern directory:
```
cd serverless-patterns/eventbridge-schedule-secret-rotation-cdk
```
5. From the command line, use AWS CDK to deploy the AWS resources for the pattern as specified in the [eventbridge_schedule_secret_rotation_stack.py](/cdk/eventbridge_schedule_secret_rotation_stack.py) file:
```
cdk deploy
```
The sample project configures the secret rotation to be triggered every hour. If you wish to use use a different value, you could do so by providing a cron/rate based expression for the *SecretRotationSchedule* param. Please refer AWS [documentation](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-scheduled-rule-pattern.html) about using cron/rate expressions.
```
cdk deploy --parameters SecretRotationSchedule="cron(* 0/1 * * ? *)"
```

6. Note the outputs from the CDK deployment process. These contain the arns for the Demo Secret and the rotation lambda.

## How it works

An EventBridge schedule is created based on the 'SecretRotationschedule' CDK parameter with the rotation lambda as the target. Rotation lambda performs the below mentioned steps on the demo secret.

- Creates a new version of the secret with AWSPENDING staging label.
- Updates the new version of the secret with the new value.
- Updates current version of the secret in AWSCURRENT stage to AWSPREVIOUS.
- Updates the staging label on the new version of secret from AWSPENDING to AWSCURRENT

## Testing

- Copy *SecretArn* value from the cdk output.
- Run the following command from the command line to retrive the secret value

```
aws secretsmanager get-secret-value --secret-id="{SecretArn}"
```
- Run the following command to view the versions created for the secret as part of rotation process

```
aws secretsmanager describe-secret --secret-id="{SecretArn}"
```

## Cleanup

1. Delete the stack
```
cdk destroy EventbridgeScheduleSecretRotationCdkStack
```
senthilkumarmohan marked this conversation as resolved.
Show resolved Hide resolved
2. Deactivate the Python virtual environment. For instructions, please refer this [page](https://python.land/virtual-environments/virtualenv#Python_venv_activation)

----
Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.

SPDX-License-Identifier: MIT-0
28 changes: 28 additions & 0 deletions eventbridge-schedule-secret-rotation-cdk/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env python3
import os

import aws_cdk as cdk

from cdk.eventbridge_schedule_secret_rotation_stack import EventbridgeScheduleSecretRotationCdkStack


app = cdk.App()
EventbridgeScheduleSecretRotationCdkStack(app, "EventbridgeScheduleSecretRotationCdkStack",
# If you don't specify 'env', this stack will be environment-agnostic.
# Account/Region-dependent features and context lookups will not work,
# but a single synthesized template can be deployed anywhere.

# Uncomment the next line to specialize this stack for the AWS Account
# and Region that are implied by the current CLI configuration.

#env=cdk.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region=os.getenv('CDK_DEFAULT_REGION')),

# Uncomment the next line if you know exactly what Account and Region you
# want to deploy the stack to. */

#env=cdk.Environment(account='123456789012', region='us-east-1'),

# For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html
)

app.synth()
55 changes: 55 additions & 0 deletions eventbridge-schedule-secret-rotation-cdk/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"app": "python3 app.py",
"watch": {
"include": [
"**"
],
"exclude": [
"README.md",
"cdk*.json",
"requirements*.txt",
"source.bat",
"**/__init__.py",
"python/__pycache__",
"tests"
]
},
"context": {
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/core:target-partitions": [
"aws",
"aws-cn"
],
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
"@aws-cdk/aws-iam:minimizePolicies": true,
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
"@aws-cdk/core:enablePartitionLiterals": true,
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
"@aws-cdk/aws-iam:standardizedServicePrincipals": true,
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
"@aws-cdk/aws-route53-patters:useCertificate": true,
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
"@aws-cdk/aws-redshift:columnId": true,
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
"@aws-cdk/aws-kms:aliasNameRef": true,
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true
}
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import json
from aws_cdk import (
CfnOutput,
CfnParameter,
Duration,
Stack,
aws_iam as iam,
aws_lambda as lambda_,
aws_secretsmanager as secretsmanager,
aws_events as events,
aws_scheduler as scheduler
)
from constructs import Construct

class EventbridgeScheduleSecretRotationCdkStack(Stack):

def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)

param_rotation_schedule = CfnParameter(self, "SecretRotationSchedule",
description="Secret rotation schedule (cron or rate)",
default="cron(0 0/1 * * ? *)"
)

secret = secretsmanager.CfnSecret(self, "Secret",
secret_string = json.dumps({"value":"initial_value"}),
name=f'secret-rotation-demo-secret',
description = "Secret Rotation Demo secret")

rotation_lambda = self.create_rotation_lambda(secret)

self.setup_rotation_schedule(param_rotation_schedule, rotation_lambda)

CfnOutput(self, "SecretArn",
value=secret.attr_id)

CfnOutput(self, "SecretRotationLambdaArn",
value=rotation_lambda.function_arn)

CfnOutput(self, "RotationSchedule",
value=str(param_rotation_schedule.value_as_string))

def create_rotation_lambda(self, secret):

rotation_lambda_role = iam.Role(self, "secret-rotator-lambda-role",
role_name = f"secret-rotator-lambda-role",
assumed_by = iam.ServicePrincipal("lambda.amazonaws.com"),
managed_policies = [
iam.ManagedPolicy.from_aws_managed_policy_name("service-role/AWSLambdaBasicExecutionRole")
],
inline_policies = {
"secret-rotator-policy":
iam.PolicyDocument(
statements = [
iam.PolicyStatement(
actions = ["secretsmanager:DescribeSecret",
"secretsmanager:GetSecretValue",
"secretsmanager:PutSecretValue",
"secretsmanager:UpdateSecretVersionStage"],
resources = [secret.attr_id]
),
iam.PolicyStatement(
actions = ["events:PutEvents"],
resources = [f"arn:aws:events:{Stack.of(self).region}:{Stack.of(self).account}:event-bus/default"])
]
)
}
)

rotation_lambda = lambda_.Function(self, "secret-rotation-lambda",
role=rotation_lambda_role,
code=lambda_.Code.from_asset("src//lambda"),
handler="rotation_lambda.lambda_handler",
runtime=lambda_.Runtime.PYTHON_3_11,
environment={
"SECRET_ID": secret.attr_id
},
timeout=Duration.seconds(120)
)

lambda_.CfnPermission(self, "events-permission-on-rotator-lambda",
action="lambda:InvokeFunction",
function_name=rotation_lambda.function_name,
principal="events.amazonaws.com")

return rotation_lambda

def setup_rotation_schedule(self, param_rotation_schedule, rotation_lambda):
scheduler_role = iam.Role(self, "secret-rotation-scheduler-role",
role_name = f"secret-rotation-scheduler-role",
assumed_by = iam.ServicePrincipal("scheduler.amazonaws.com"),
inline_policies = {
"secret-rotation-scheduler-policy":
iam.PolicyDocument(
statements = [
iam.PolicyStatement(
actions = ["lambda:InvokeFunction"],
resources = [rotation_lambda.function_arn]
)
]
)
}
)
scheduler.CfnSchedule(self, "secret-rotation-scheduler",
flexible_time_window=scheduler.CfnSchedule.FlexibleTimeWindowProperty(
mode="OFF"
),
schedule_expression=param_rotation_schedule.value_as_string,
target=scheduler.CfnSchedule.TargetProperty(
arn=rotation_lambda.function_arn,
role_arn=scheduler_role.role_arn,
retry_policy=scheduler.CfnSchedule.RetryPolicyProperty(
maximum_retry_attempts=0,
maximum_event_age_in_seconds=5 * 60
)
)
)
64 changes: 64 additions & 0 deletions eventbridge-schedule-secret-rotation-cdk/example-pattern.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"title": "Secret rotation in AMS Secrets Manager using Amazon EventBridge Scheduler and AWS Lambda",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The title can be max upto 75 char.

"description": "Secret rotation in AMS Secrets Manager using Amazon EventBridge Scheduler and AWS Lambda",
"language": "Python",
"level": "200",
"framework": "CDK",
"introBox": {
"headline": "How it works",
"text": [
"This sample project demonstrates rotating secrets in AWS Secrests Manager using Amazon EventBridge Scheduler and AWS Lambda at desired intervals for several use cases. e.g. rotating OAtuth tokens with limited lifespan.",
"An EventBridge schedule is created based on the 'Secret rotation schedule' CDK parameter with rotation Lambda as the target which does the job",
"You could provide the interval at which you want the secret to be rotated as a cron or rate expression."
]
},
"gitHub": {
"template": {
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/eventbridge-schedule-secret-rotation-cdk",
"templateURL": "cdk/eventbridge_schedule_secret_rotation_stack.py",
"projectFolder": "eventbridge-schedule-secret-rotation-cdk",
"templateFile": "template.yaml"
senthilkumarmohan marked this conversation as resolved.
Show resolved Hide resolved
}
},
"deploy": {
"text": [
"cdk deploy"
]
},
"testing": {
"text": [
"See the GitHub repo for detailed testing instructions."
]
},
"cleanup": {
"text": [
"Delete the stack: <code>cdk destroy</code>."
]
},
"authors": [
{
"name": "Senthil Mohan",
"image": "https://ca.slack-edge.com/E015GUGD2V6-U03JTLBU1HD-ffd97b7877e5-512",
"bio": "Senthil is a Solutions Architect at AWS working with ISVs in the UK to migrate, modernize and build their software products on AWS.",
"linkedin": "senthil-kumar-mohan"
},
{
"name": "Shubhankar Sumar",
"image": "https://ca.slack-edge.com/E015GUGD2V6-W01BSTRT7EZ-3a3d16cf4fa5-192",
"bio": "Shubhankar is a Sr. Solutions Architect at AWS working with ISVs in the UK to build, run, and scale their software products on AWS.",
"linkedin": "shubhankar-sumar"
}
],
"resources": {
"bullets": [
{
"text": "Using cron and rate expressions to schedule rules in Amazon EventBridge",
"link": "https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-scheduled-rule-pattern.html"
},
{
"text":"AWS Secrets Manager rotation template",
"link": "https://github.com/aws-samples/aws-secrets-manager-rotation-lambdas/blob/master/SecretsManagerRotationTemplate/lambda_function.py"
}
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pytest==6.2.5
3 changes: 3 additions & 0 deletions eventbridge-schedule-secret-rotation-cdk/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
aws-cdk-lib==2.89.0
constructs>=10.0.0,<11.0.0
boto3
Loading