Skip to content

Commit

Permalink
feat(custom-resources): implement IGrantable for AwsCustomResource (#…
Browse files Browse the repository at this point in the history
…4790)

* feat(custom-resources): implement IGrantable for AwsCustomResource

Closes #4710

* remove IPrincipal

* doc
  • Loading branch information
jogold authored and mergify[bot] committed Nov 4, 2019
1 parent c31ca27 commit b840784
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 7 deletions.
7 changes: 6 additions & 1 deletion packages/@aws-cdk/custom-resources/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,12 @@ getParameter.getData('Parameter.Value')
IAM policy statements required to make the API calls are derived from the calls
and allow by default the actions to be made on all resources (`*`). You can
restrict the permissions by specifying your own list of statements with the
`policyStatements` prop.
`policyStatements` prop. The custom resource also implements `iam.IGrantable`,
making it possible to use the `grantXxx()` methods.

As this custom resource uses a singleton Lambda function, it's important to note
that the function's role will eventually accumulate the permissions/grants from all
resources.

Chained API calls can be achieved by creating dependencies:
```ts
Expand Down
22 changes: 16 additions & 6 deletions packages/@aws-cdk/custom-resources/lib/aws-custom-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export interface AwsSdkCall {
*
* Example for ECS / updateService: 'service.deploymentConfiguration.maximumPercent'
*
* @default return all data
* @default - return all data
*/
readonly outputPath?: string;
}
Expand All @@ -99,29 +99,36 @@ export interface AwsCustomResourceProps {
* The AWS SDK call to make when the resource is created.
* At least onCreate, onUpdate or onDelete must be specified.
*
* @default the call when the resource is updated
* @default - the call when the resource is updated
*/
readonly onCreate?: AwsSdkCall;

/**
* The AWS SDK call to make when the resource is updated
*
* @default no call
* @default - no call
*/
readonly onUpdate?: AwsSdkCall;

/**
* The AWS SDK call to make when the resource is deleted
*
* @default no call
* @default - no call
*/
readonly onDelete?: AwsSdkCall;

/**
* The IAM policy statements to allow the different calls. Use only if
* resource restriction is needed.
*
* @default extract the permissions from the calls
* The custom resource also implements `iam.IGrantable`, making it possible
* to use the `grantXxx()` methods.
*
* As this custom resource uses a singleton Lambda function, it's important
* to note the that function's role will eventually accumulate the
* permissions/grants from all resources.
*
* @default - extract the permissions from the calls
*/
readonly policyStatements?: iam.PolicyStatement[];

Expand All @@ -133,7 +140,9 @@ export interface AwsCustomResourceProps {
readonly timeout?: cdk.Duration
}

export class AwsCustomResource extends cdk.Construct {
export class AwsCustomResource extends cdk.Construct implements iam.IGrantable {
public readonly grantPrincipal: iam.IPrincipal;

private readonly customResource: CustomResource;

constructor(scope: cdk.Construct, id: string, props: AwsCustomResourceProps) {
Expand All @@ -157,6 +166,7 @@ export class AwsCustomResource extends cdk.Construct {
lambdaPurpose: 'AWS',
timeout: props.timeout || cdk.Duration.seconds(30),
});
this.grantPrincipal = provider.grantPrincipal;

if (props.policyStatements) {
for (const statement of props.policyStatements) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,49 @@ export = {
Timeout: 900
}));

test.done();
},

'implements IGrantable'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const role = new iam.Role(stack, 'Role', {
assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com')
});
const customResource = new AwsCustomResource(stack, 'AwsSdk', {
onCreate: {
service: 'service',
action: 'action',
physicalResourceId: 'id'
}
});

// WHEN
role.grantPassRole(customResource.grantPrincipal);

expect(stack).to(haveResource('AWS::IAM::Policy', {
PolicyDocument: {
Statement: [
{
Action: 'service:Action',
Effect: 'Allow',
Resource: '*'
},
{
Action: 'iam:PassRole',
Effect: 'Allow',
Resource: {
'Fn::GetAtt': [
'Role1ABCC5F0',
'Arn'
]
}
}
],
Version: '2012-10-17'
}
}));

test.done();
}
};

0 comments on commit b840784

Please sign in to comment.