From 58b7f08324f49da35d522b8789522751240b10c0 Mon Sep 17 00:00:00 2001 From: Senthil Kumar Mohan Date: Fri, 13 Sep 2024 14:11:35 +0100 Subject: [PATCH 1/5] New pattern: eventbridge-schedule-secret-rotation-cdk --- .../README.md | 70 ++++++++++ .../app.py | 28 ++++ .../cdk.json | 55 ++++++++ .../cdk/__init__.py | 0 ...ntbridge_schedule_secret_rotation_stack.py | 117 +++++++++++++++++ .../example-pattern.json | 64 +++++++++ .../requirements-dev.txt | 1 + .../requirements.txt | 3 + .../src/lambda/rotation_lambda.py | 123 ++++++++++++++++++ 9 files changed, 461 insertions(+) create mode 100644 eventbridge-schedule-secret-rotation-cdk/README.md create mode 100644 eventbridge-schedule-secret-rotation-cdk/app.py create mode 100644 eventbridge-schedule-secret-rotation-cdk/cdk.json create mode 100644 eventbridge-schedule-secret-rotation-cdk/cdk/__init__.py create mode 100644 eventbridge-schedule-secret-rotation-cdk/cdk/eventbridge_schedule_secret_rotation_stack.py create mode 100644 eventbridge-schedule-secret-rotation-cdk/example-pattern.json create mode 100644 eventbridge-schedule-secret-rotation-cdk/requirements-dev.txt create mode 100644 eventbridge-schedule-secret-rotation-cdk/requirements.txt create mode 100644 eventbridge-schedule-secret-rotation-cdk/src/lambda/rotation_lambda.py diff --git a/eventbridge-schedule-secret-rotation-cdk/README.md b/eventbridge-schedule-secret-rotation-cdk/README.md new file mode 100644 index 000000000..cd4c82d0d --- /dev/null +++ b/eventbridge-schedule-secret-rotation-cdk/README.md @@ -0,0 +1,70 @@ +# EventBridge Scheduler to Lambda to Secrets Manager + +This sample project demonstrates rotating secrets using AWS 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 + +## 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. Change directory to the pattern directory: + ``` + cd serverless-patterns/eventbridge-schedule-secret-rotation-cdk + ``` +3. From the command line, use AWS CDK to deploy the AWS resources for the pattern as specified in the [EventbridgeScheduleSecretRotationCdkStack.py](/cdk/EventbridgeScheduleSecretRotationCdkStack.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 * * ? *)" + ``` + +4. 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 *DemoSecretArn* 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="{DemoSecretArn}" + ``` +- Run the following command to view the versions created for the secret as part of rotation process + + ``` + aws secretsmanager describe-secret --secret-id="{DemoSecretArn}" + ``` + +## Cleanup + +1. Delete the stack + ``` + cdk destroy EventbridgeScheduleSecretRotationCdkStack + ``` + +---- +Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/eventbridge-schedule-secret-rotation-cdk/app.py b/eventbridge-schedule-secret-rotation-cdk/app.py new file mode 100644 index 000000000..a5b214930 --- /dev/null +++ b/eventbridge-schedule-secret-rotation-cdk/app.py @@ -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() diff --git a/eventbridge-schedule-secret-rotation-cdk/cdk.json b/eventbridge-schedule-secret-rotation-cdk/cdk.json new file mode 100644 index 000000000..4f37309af --- /dev/null +++ b/eventbridge-schedule-secret-rotation-cdk/cdk.json @@ -0,0 +1,55 @@ +{ + "app": "python 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 + } +} diff --git a/eventbridge-schedule-secret-rotation-cdk/cdk/__init__.py b/eventbridge-schedule-secret-rotation-cdk/cdk/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/eventbridge-schedule-secret-rotation-cdk/cdk/eventbridge_schedule_secret_rotation_stack.py b/eventbridge-schedule-secret-rotation-cdk/cdk/eventbridge_schedule_secret_rotation_stack.py new file mode 100644 index 000000000..a56548a2a --- /dev/null +++ b/eventbridge-schedule-secret-rotation-cdk/cdk/eventbridge_schedule_secret_rotation_stack.py @@ -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 + ) + ) + ) \ No newline at end of file diff --git a/eventbridge-schedule-secret-rotation-cdk/example-pattern.json b/eventbridge-schedule-secret-rotation-cdk/example-pattern.json new file mode 100644 index 000000000..fcd9b119f --- /dev/null +++ b/eventbridge-schedule-secret-rotation-cdk/example-pattern.json @@ -0,0 +1,64 @@ +{ + "title": "Secret rotation using EventBridge Scheduler and Lambda", + "description": "Secret rotation using EventBridge Scheduler and Lambda", + "language": "Python", + "level": "200", + "framework": "CDK", + "introBox": { + "headline": "How it works", + "text": [ + "This sample project demonstrates rotating secrets using AWS Amazon EventBridge Scheduler and AWS Lambda at desired intervals.", + "An EventBridge schedule is created based on the 'Secret rotation schedule' CDK parameter with rotation lambda as the target.", + "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": "serverless-patterns/eventbridge-schedule-secret-rotation-cdk", + "projectFolder": "eventbridge-schedule-secret-rotation-cdk", + "templateFile": "template.yaml" + } + }, + "deploy": { + "text": [ + "cdk deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: cdk destroy." + ] + }, + "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" + } + ] + } + } \ No newline at end of file diff --git a/eventbridge-schedule-secret-rotation-cdk/requirements-dev.txt b/eventbridge-schedule-secret-rotation-cdk/requirements-dev.txt new file mode 100644 index 000000000..927094516 --- /dev/null +++ b/eventbridge-schedule-secret-rotation-cdk/requirements-dev.txt @@ -0,0 +1 @@ +pytest==6.2.5 diff --git a/eventbridge-schedule-secret-rotation-cdk/requirements.txt b/eventbridge-schedule-secret-rotation-cdk/requirements.txt new file mode 100644 index 000000000..3d8bd9b67 --- /dev/null +++ b/eventbridge-schedule-secret-rotation-cdk/requirements.txt @@ -0,0 +1,3 @@ +aws-cdk-lib==2.89.0 +constructs>=10.0.0,<11.0.0 +boto3 \ No newline at end of file diff --git a/eventbridge-schedule-secret-rotation-cdk/src/lambda/rotation_lambda.py b/eventbridge-schedule-secret-rotation-cdk/src/lambda/rotation_lambda.py new file mode 100644 index 000000000..ffa9a2dbb --- /dev/null +++ b/eventbridge-schedule-secret-rotation-cdk/src/lambda/rotation_lambda.py @@ -0,0 +1,123 @@ +import logging +import base64 +import sys +import boto3 +import logging +import os +import json +import uuid +import datetime + +logger = logging.getLogger("") + +pending_version = None + +steps = ['createSecret', 'setSecret', 'testSecret', 'finishSecret'] + +def lambda_handler(event, context): + service_client = boto3.client('secretsmanager') + arn = os.environ['SECRET_ID'] + pending_version = get_secret_version_id_by_stage(arn, "AWSPENDING") + logger.info(f'pending_version: {pending_version}') + + if pending_version == None: + pending_version = str(uuid.uuid4()) + + create_secret(service_client, arn, pending_version) + set_secret(service_client, arn, pending_version) + test_secret(service_client, arn, pending_version) + finish_secret(service_client, arn, pending_version) + +def get_secret_version_id_by_stage(secret_id, version_stage): + secrets_manager_client = boto3.client('secretsmanager') + metadata = secrets_manager_client.describe_secret(SecretId=secret_id) + versions = metadata['VersionIdsToStages'] + stage_vesrion_id = None + for version in versions: + if version_stage in versions[version]: + stage_vesrion_id = version + break + return stage_vesrion_id + +def create_secret(service_client, arn, pending_version): + try: + service_client.get_secret_value(SecretId=arn, + VersionId=pending_version, + VersionStage="AWSPENDING") + logger.info("createSecret: Successfully retrieved secret for %s." % arn) + except Exception as e: + # Put the secret + new_secret = {"value":f'secret_{datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}'} + service_client.put_secret_value(SecretId=arn, + ClientRequestToken=pending_version, + SecretString=json.dumps(new_secret), + VersionStages=["AWSPENDING"]) + logger.info("createSecret: Successfully put secret for ARN %s and version %s." % (arn, pending_version)) + +def set_secret(service_client, arn, pending_version): + """Set the secret + + This method should set the AWSPENDING secret in the service that the secret belongs to. + For example, if the secret is a database credential, this method should take the value of the AWSPENDING secret and + set the user's password to this value in the database. + + Args: + service_client (client): The secrets manager service client + + arn (string): The secret ARN or other identifier + + clientRequestToken (string): The ClientRequestToken associated with the secret version + + """ + logger.info("setSecret: Successfully set secret for %s." % arn) + + +def test_secret(service_client, arn, pending_version): + """Test the secret + + This method should validate that the AWSPENDING secret works in the service that the secret belongs to. + For example, if the secret is a database credential, this method should validate that the user can login with the password + in AWSPENDING and that the user has all of the expected permissions against the database. + + Args: + service_client (client): The secrets manager service client + + arn (string): The secret ARN or other identifier + + clientRequestToken (string): The ClientRequestToken associated with the secret version + + """ + logger.info("testSecret: Successfully tested secret for %s." % arn) + +def finish_secret(service_client, arn, pending_version): + """Finish the secret + + This method finalizes the rotation process by marking the secret version passed in as the AWSCURRENT secret. + + Args: + service_client (client): The secrets manager service client + + arn (string): The secret ARN or other identifier + + clientRequestToken (string): The ClientRequestToken associated with the secret version + + Raises: + ResourceNotFoundException: If the secret with the specified arn does not exist + + """ + try: + current_version_id = get_secret_version_id_by_stage(arn, "AWSCURRENT") + service_client.update_secret_version_stage(SecretId=arn, + VersionStage="AWSCURRENT", + MoveToVersionId=pending_version, + RemoveFromVersionId=current_version_id) + + service_client.update_secret_version_stage(SecretId=arn, + VersionStage="AWSPENDING", + RemoveFromVersionId=pending_version) + logger.info("finishSecret: Successfully set AWSCURRENT for secret %s." % arn) + except Exception as e: + logger.error("finishSecret: Failed to set AWSCURRENT for secret %s." % arn) + +if __name__ == "__main__": + lambda_handler({}, []) \ No newline at end of file From 26edfb799cfd138875d568c4a8f39513ebd16495 Mon Sep 17 00:00:00 2001 From: Senthil Kumar Mohan Date: Fri, 13 Sep 2024 14:35:01 +0100 Subject: [PATCH 2/5] updated file path --- eventbridge-schedule-secret-rotation-cdk/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eventbridge-schedule-secret-rotation-cdk/README.md b/eventbridge-schedule-secret-rotation-cdk/README.md index cd4c82d0d..59eb9b38b 100644 --- a/eventbridge-schedule-secret-rotation-cdk/README.md +++ b/eventbridge-schedule-secret-rotation-cdk/README.md @@ -23,7 +23,7 @@ Important: this application uses various AWS services and there are costs associ ``` cd serverless-patterns/eventbridge-schedule-secret-rotation-cdk ``` -3. From the command line, use AWS CDK to deploy the AWS resources for the pattern as specified in the [EventbridgeScheduleSecretRotationCdkStack.py](/cdk/EventbridgeScheduleSecretRotationCdkStack.py) file: +3. 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 ``` From 731cf16ed01a2243140d7fd044aad816bd12e27e Mon Sep 17 00:00:00 2001 From: Shubhankar Sumar Date: Mon, 14 Oct 2024 17:33:38 +0100 Subject: [PATCH 3/5] Addressed review comments --- .../README.md | 27 ++++++++++++------- .../cdk.json | 2 +- .../example-pattern.json | 10 +++---- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/eventbridge-schedule-secret-rotation-cdk/README.md b/eventbridge-schedule-secret-rotation-cdk/README.md index 59eb9b38b..7d074e802 100644 --- a/eventbridge-schedule-secret-rotation-cdk/README.md +++ b/eventbridge-schedule-secret-rotation-cdk/README.md @@ -1,6 +1,6 @@ -# EventBridge Scheduler to Lambda to Secrets Manager +# Amazon EventBridge Scheduler to AWS Lambda to AWS Secrets Manager -This sample project demonstrates rotating secrets using AWS Amazon EventBridge Scheduler and AWS Lambda at desired intervals. +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) @@ -19,11 +19,19 @@ Important: this application uses various AWS services and there are costs associ ``` git clone https://github.com/aws-samples/serverless-patterns ``` -2. Change directory to the pattern directory: + +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 ``` -3. 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: +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 ``` @@ -32,7 +40,7 @@ Important: this application uses various AWS services and there are costs associ cdk deploy --parameters SecretRotationSchedule="cron(* 0/1 * * ? *)" ``` -4. Note the outputs from the CDK deployment process. These contain the arns for the Demo Secret and the rotation lambda. +6. Note the outputs from the CDK deployment process. These contain the arns for the Demo Secret and the rotation lambda. ## How it works @@ -45,16 +53,16 @@ An EventBridge schedule is created based on the 'SecretRotationschedule' CDK par ## Testing -- Copy *DemoSecretArn* value from the cdk output. +- 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="{DemoSecretArn}" + 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="{DemoSecretArn}" + aws secretsmanager describe-secret --secret-id="{SecretArn}" ``` ## Cleanup @@ -63,7 +71,8 @@ An EventBridge schedule is created based on the 'SecretRotationschedule' CDK par ``` cdk destroy EventbridgeScheduleSecretRotationCdkStack ``` - +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. diff --git a/eventbridge-schedule-secret-rotation-cdk/cdk.json b/eventbridge-schedule-secret-rotation-cdk/cdk.json index 4f37309af..aaa52df89 100644 --- a/eventbridge-schedule-secret-rotation-cdk/cdk.json +++ b/eventbridge-schedule-secret-rotation-cdk/cdk.json @@ -1,5 +1,5 @@ { - "app": "python app.py", + "app": "python3 app.py", "watch": { "include": [ "**" diff --git a/eventbridge-schedule-secret-rotation-cdk/example-pattern.json b/eventbridge-schedule-secret-rotation-cdk/example-pattern.json index fcd9b119f..005e3570b 100644 --- a/eventbridge-schedule-secret-rotation-cdk/example-pattern.json +++ b/eventbridge-schedule-secret-rotation-cdk/example-pattern.json @@ -1,21 +1,21 @@ { - "title": "Secret rotation using EventBridge Scheduler and Lambda", - "description": "Secret rotation using EventBridge Scheduler and Lambda", + "title": "Secret rotation in AMS Secrets Manager using Amazon EventBridge Scheduler and AWS Lambda", + "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 using AWS Amazon EventBridge Scheduler and AWS Lambda at desired intervals.", - "An EventBridge schedule is created based on the 'Secret rotation schedule' CDK parameter with rotation lambda as the target.", + "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": "serverless-patterns/eventbridge-schedule-secret-rotation-cdk", + "templateURL": "cdk/eventbridge_schedule_secret_rotation_stack.py", "projectFolder": "eventbridge-schedule-secret-rotation-cdk", "templateFile": "template.yaml" } From 7f6e3f6591cea7ad8169d739d347110127b65547 Mon Sep 17 00:00:00 2001 From: Senthil Kumar Mohan Date: Fri, 25 Oct 2024 12:24:36 +0100 Subject: [PATCH 4/5] Review comments --- .../example-pattern.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/eventbridge-schedule-secret-rotation-cdk/example-pattern.json b/eventbridge-schedule-secret-rotation-cdk/example-pattern.json index 005e3570b..11688f7b0 100644 --- a/eventbridge-schedule-secret-rotation-cdk/example-pattern.json +++ b/eventbridge-schedule-secret-rotation-cdk/example-pattern.json @@ -1,5 +1,5 @@ { - "title": "Secret rotation in AMS Secrets Manager using Amazon EventBridge Scheduler and AWS Lambda", + "title": "Secret rotation using Amazon EventBridge Scheduler and AWS Lambda", "description": "Secret rotation in AMS Secrets Manager using Amazon EventBridge Scheduler and AWS Lambda", "language": "Python", "level": "200", @@ -15,9 +15,9 @@ "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", + "templateURL": "serverless-patterns/eventbridge-schedule-secret-rotation-cdk", "projectFolder": "eventbridge-schedule-secret-rotation-cdk", - "templateFile": "template.yaml" + "templateFile": "cdk/eventbridge_schedule_secret_rotation_stack.py" } }, "deploy": { From 6235cf2db421bc015764f6606455228b2d7fcbc0 Mon Sep 17 00:00:00 2001 From: biswanathmukherjee <30332793+biswanathmukherjee@users.noreply.github.com> Date: Fri, 25 Oct 2024 17:11:49 +0530 Subject: [PATCH 5/5] Create eventbridge-schedule-secret-rotation-cdk.json --- ...ntbridge-schedule-secret-rotation-cdk.json | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 eventbridge-schedule-secret-rotation-cdk/eventbridge-schedule-secret-rotation-cdk.json diff --git a/eventbridge-schedule-secret-rotation-cdk/eventbridge-schedule-secret-rotation-cdk.json b/eventbridge-schedule-secret-rotation-cdk/eventbridge-schedule-secret-rotation-cdk.json new file mode 100644 index 000000000..59726f7d7 --- /dev/null +++ b/eventbridge-schedule-secret-rotation-cdk/eventbridge-schedule-secret-rotation-cdk.json @@ -0,0 +1,94 @@ +{ + "title": "Secret rotation using Amazon EventBridge Scheduler and AWS Lambda", + "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": "serverless-patterns/eventbridge-schedule-secret-rotation-cdk", + "projectFolder": "eventbridge-schedule-secret-rotation-cdk", + "templateFile": "cdk/eventbridge_schedule_secret_rotation_stack.py" + } + }, + "deploy": { + "text": [ + "cdk deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: cdk destroy." + ] + }, + "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" + } + ] + }, + "patternArch": { + "icon1": { + "x": 20, + "y": 50, + "service": "eventbridge-scheduler", + "label": "Amazon EventBridge Scheduler" + }, + "icon2": { + "x": 50, + "y": 50, + "service": "lambda", + "label": "AWS Lambda" + }, + "icon3": { + "x": 80, + "y": 50, + "service": "secretsmanager", + "label": "AWS Secrets Manager" + }, + "line1": { + "from": "icon1", + "to": "icon2", + "label": "trigger" + }, + "line2": { + "from": "icon2", + "to": "icon3", + "label": "rotate secret" + } + } +}