diff --git a/packages/@aws-cdk/aws-secretsmanager/README.md b/packages/@aws-cdk/aws-secretsmanager/README.md index 0c0f45828e9ef..81c8e3c5e8e24 100644 --- a/packages/@aws-cdk/aws-secretsmanager/README.md +++ b/packages/@aws-cdk/aws-secretsmanager/README.md @@ -87,6 +87,8 @@ secret.addRotationSchedule('RotationSchedule', { }); ``` +Note: The required permissions for Lambda to call SecretsManager and the other way round are automatically granted based on [AWS Documentation](https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-required-permissions.html) as long as the Lambda is not imported. + See [Overview of the Lambda Rotation Function](https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html) on how to implement a Lambda Rotation Function. ### Using a Hosted Lambda Function diff --git a/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts b/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts index 1243976963386..7e00492f2cb2f 100644 --- a/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts +++ b/packages/@aws-cdk/aws-secretsmanager/lib/rotation-schedule.ts @@ -1,4 +1,5 @@ import * as ec2 from '@aws-cdk/aws-ec2'; +import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import { Duration, Resource, Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; @@ -70,6 +71,35 @@ export class RotationSchedule extends Resource { throw new Error('One of `rotationLambda` or `hostedRotation` must be specified.'); } + if (props.rotationLambda?.permissionsNode.defaultChild) { + props.rotationLambda.grantInvoke(new iam.ServicePrincipal('secretsmanager.amazonaws.com')); + + props.rotationLambda.addToRolePolicy( + new iam.PolicyStatement({ + actions: [ + 'secretsmanager:DescribeSecret', + 'secretsmanager:GetSecretValue', + 'secretsmanager:PutSecretValue', + 'secretsmanager:UpdateSecretVersionStage', + ], + resources: [props.secret.secretArn], + conditions: { + StringEquals: { + 'secretsmanager:resource/AllowRotationLambdaArn': props.rotationLambda.functionArn, + }, + }, + }), + ); + props.rotationLambda.addToRolePolicy( + new iam.PolicyStatement({ + actions: [ + 'secretsmanager:GetRandomPassword', + ], + resources: ['*'], + }), + ); + } + new CfnRotationSchedule(this, 'Resource', { secretId: props.secret.secretArn, rotationLambdaArn: props.rotationLambda?.functionArn, diff --git a/packages/@aws-cdk/aws-secretsmanager/test/rotation-schedule.test.ts b/packages/@aws-cdk/aws-secretsmanager/test/rotation-schedule.test.ts index e77336732d51b..3ab3422cd265b 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/rotation-schedule.test.ts +++ b/packages/@aws-cdk/aws-secretsmanager/test/rotation-schedule.test.ts @@ -41,6 +41,75 @@ test('create a rotation schedule with a rotation Lambda', () => { }); }); +test('assign permissions for rotation schedule with a rotation Lambda', () => { + // GIVEN + const secret = new secretsmanager.Secret(stack, 'Secret'); + const rotationLambda = new lambda.Function(stack, 'Lambda', { + runtime: lambda.Runtime.NODEJS_10_X, + code: lambda.Code.fromInline('export.handler = event => event;'), + handler: 'index.handler', + }); + + // WHEN + new secretsmanager.RotationSchedule(stack, 'RotationSchedule', { + secret, + rotationLambda, + }); + + // THEN + expect(stack).toHaveResource('AWS::Lambda::Permission', { + Action: 'lambda:InvokeFunction', + FunctionName: { + 'Fn::GetAtt': [ + 'LambdaD247545B', + 'Arn', + ], + }, + Principal: 'secretsmanager.amazonaws.com', + }); + + expect(stack).toHaveResource('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: [ + 'secretsmanager:DescribeSecret', + 'secretsmanager:GetSecretValue', + 'secretsmanager:PutSecretValue', + 'secretsmanager:UpdateSecretVersionStage', + ], + Effect: 'Allow', + Resource: { + Ref: 'SecretA720EF05', + }, + Condition: { + StringEquals: { + 'secretsmanager:resource/AllowRotationLambdaArn': { + 'Fn::GetAtt': [ + 'LambdaD247545B', + 'Arn', + ], + }, + }, + }, + }, + { + Action: 'secretsmanager:GetRandomPassword', + Effect: 'Allow', + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'LambdaServiceRoleDefaultPolicyDAE46E21', + Roles: [ + { + Ref: 'LambdaServiceRoleA8ED4D3B', + }, + ], + }); +}); + describe('hosted rotation', () => { test('single user not in a vpc', () => { // GIVEN