Skip to content

Commit a205831

Browse files
author
Daniel Neilson
committed
feat(secretsmanager): adds grantWrite to Secret
1 parent 5460717 commit a205831

File tree

2 files changed

+235
-0
lines changed

2 files changed

+235
-0
lines changed

packages/@aws-cdk/aws-secretsmanager/lib/secret.ts

+26
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ export interface ISecret extends IResource {
4141
*/
4242
grantRead(grantee: iam.IGrantable, versionStages?: string[]): iam.Grant;
4343

44+
/**
45+
* Grants writing the secret value to some role.
46+
*
47+
* @param grantee the principal being granted permission.
48+
*/
49+
grantWrite(grantee: iam.IGrantable): iam.Grant;
50+
4451
/**
4552
* Adds a rotation schedule to the secret.
4653
*/
@@ -148,6 +155,25 @@ abstract class SecretBase extends Resource implements ISecret {
148155
return result;
149156
}
150157

158+
public grantWrite(grantee: iam.IGrantable): iam.Grant {
159+
// @see https://docs.aws.amazon.com/secretsmanager/latest/userguide/auth-and-access_identity-based-policies.html
160+
const result = iam.Grant.addToPrincipal({
161+
grantee,
162+
actions: ['secretsmanager:PutSecretValue'],
163+
resourceArns: [this.secretArn],
164+
scope: this,
165+
});
166+
167+
if (this.encryptionKey) {
168+
// @see https://docs.aws.amazon.com/kms/latest/developerguide/services-secrets-manager.html
169+
this.encryptionKey.grantEncrypt(
170+
new kms.ViaServicePrincipal(`secretsmanager.${Stack.of(this).region}.amazonaws.com`, grantee.grantPrincipal),
171+
);
172+
}
173+
174+
return result;
175+
}
176+
151177
public get secretValue() {
152178
return this.secretValueFromJson('');
153179
}

packages/@aws-cdk/aws-secretsmanager/test/test.secret.ts

+209
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,215 @@ export = {
435435
test.done();
436436
},
437437

438+
'grantWrite'(test: Test) {
439+
// GIVEN
440+
const stack = new cdk.Stack();
441+
const secret = new secretsmanager.Secret(stack, 'Secret', {});
442+
const role = new iam.Role(stack, 'Role', { assumedBy: new iam.AccountRootPrincipal() });
443+
444+
// WHEN
445+
secret.grantWrite(role);
446+
447+
// THEN
448+
expect(stack).to(haveResource('AWS::IAM::Policy', {
449+
PolicyDocument: {
450+
Version: '2012-10-17',
451+
Statement: [{
452+
Action: 'secretsmanager:PutSecretValue',
453+
Effect: 'Allow',
454+
Resource: { Ref: 'SecretA720EF05' },
455+
}],
456+
},
457+
}));
458+
test.done();
459+
},
460+
461+
'grantWrite with kms'(test: Test) {
462+
// GIVEN
463+
const stack = new cdk.Stack();
464+
const key = new kms.Key(stack, 'KMS');
465+
const secret = new secretsmanager.Secret(stack, 'Secret', { encryptionKey: key });
466+
const role = new iam.Role(stack, 'Role', { assumedBy: new iam.AccountRootPrincipal() });
467+
468+
// WHEN
469+
secret.grantWrite(role);
470+
471+
// THEN
472+
const expectStack = expect(stack);
473+
expectStack.to(haveResource('AWS::IAM::Policy', {
474+
PolicyDocument: {
475+
Version: '2012-10-17',
476+
Statement: [{
477+
Action: 'secretsmanager:PutSecretValue',
478+
Effect: 'Allow',
479+
Resource: { Ref: 'SecretA720EF05' },
480+
}],
481+
},
482+
}));
483+
expectStack.to(haveResource('AWS::KMS::Key', {
484+
KeyPolicy: {
485+
Statement: [{
486+
Action: [
487+
'kms:Create*',
488+
'kms:Describe*',
489+
'kms:Enable*',
490+
'kms:List*',
491+
'kms:Put*',
492+
'kms:Update*',
493+
'kms:Revoke*',
494+
'kms:Disable*',
495+
'kms:Get*',
496+
'kms:Delete*',
497+
'kms:ScheduleKeyDeletion',
498+
'kms:CancelKeyDeletion',
499+
'kms:GenerateDataKey',
500+
'kms:TagResource',
501+
'kms:UntagResource',
502+
],
503+
Effect: 'Allow',
504+
Principal: {
505+
AWS: {
506+
'Fn::Join': [
507+
'',
508+
[
509+
'arn:',
510+
{
511+
Ref: 'AWS::Partition',
512+
},
513+
':iam::',
514+
{
515+
Ref: 'AWS::AccountId',
516+
},
517+
':root',
518+
],
519+
],
520+
},
521+
},
522+
Resource: '*',
523+
}, {
524+
Effect: 'Allow',
525+
Resource: '*',
526+
Action: [
527+
'kms:Decrypt',
528+
'kms:Encrypt',
529+
'kms:ReEncrypt*',
530+
'kms:GenerateDataKey*',
531+
],
532+
Principal: {
533+
AWS: {
534+
'Fn::Join': [
535+
'',
536+
[
537+
'arn:',
538+
{
539+
Ref: 'AWS::Partition',
540+
},
541+
':iam::',
542+
{
543+
Ref: 'AWS::AccountId',
544+
},
545+
':root',
546+
],
547+
],
548+
},
549+
},
550+
Condition: {
551+
StringEquals: {
552+
'kms:ViaService': {
553+
'Fn::Join': [
554+
'',
555+
[
556+
'secretsmanager.',
557+
{
558+
Ref: 'AWS::Region',
559+
},
560+
'.amazonaws.com',
561+
],
562+
],
563+
},
564+
},
565+
},
566+
}, {
567+
Effect: 'Allow',
568+
Resource: '*',
569+
Action: [
570+
'kms:CreateGrant',
571+
'kms:DescribeKey',
572+
],
573+
Principal: {
574+
AWS: {
575+
'Fn::Join': [
576+
'',
577+
[
578+
'arn:',
579+
{
580+
Ref: 'AWS::Partition',
581+
},
582+
':iam::',
583+
{
584+
Ref: 'AWS::AccountId',
585+
},
586+
':root',
587+
],
588+
],
589+
},
590+
},
591+
Condition: {
592+
StringEquals: {
593+
'kms:ViaService': {
594+
'Fn::Join': [
595+
'',
596+
[
597+
'secretsmanager.',
598+
{
599+
Ref: 'AWS::Region',
600+
},
601+
'.amazonaws.com',
602+
],
603+
],
604+
},
605+
},
606+
},
607+
}, {
608+
Action: [
609+
'kms:Encrypt',
610+
'kms:ReEncrypt*',
611+
'kms:GenerateDataKey*',
612+
],
613+
Condition: {
614+
StringEquals: {
615+
'kms:ViaService': {
616+
'Fn::Join': [
617+
'',
618+
[
619+
'secretsmanager.',
620+
{
621+
Ref: 'AWS::Region',
622+
},
623+
'.amazonaws.com',
624+
],
625+
],
626+
},
627+
},
628+
},
629+
Effect: 'Allow',
630+
Principal: {
631+
AWS: {
632+
'Fn::GetAtt': [
633+
'Role1ABCC5F0',
634+
'Arn',
635+
],
636+
},
637+
},
638+
Resource: '*',
639+
},
640+
],
641+
Version: '2012-10-17',
642+
},
643+
}));
644+
test.done();
645+
},
646+
438647
'secretValue'(test: Test) {
439648
// GIVEN
440649
const stack = new cdk.Stack();

0 commit comments

Comments
 (0)