diff --git a/packages/@aws-cdk/aws-apigateway/lib/deployment.ts b/packages/@aws-cdk/aws-apigateway/lib/deployment.ts index 6de9f7d71cfec..133846de42c6a 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/deployment.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/deployment.ts @@ -1,4 +1,4 @@ -import { Construct, Lazy, RemovalPolicy, Resource, Stack } from '@aws-cdk/core'; +import { Construct, DefaultTokenResolver, Lazy, RemovalPolicy, Resource, Stack, StringConcat, Tokenization } from '@aws-cdk/core'; import crypto = require('crypto'); import { CfnDeployment, CfnDeploymentProps } from './apigateway.generated'; import { IRestApi } from './restapi'; @@ -128,12 +128,23 @@ class LatestDeploymentResource extends CfnDeployment { if (this.hashComponents.length > 0) { const md5 = crypto.createHash('md5'); this.hashComponents - .map(c => stack.resolve(c)) + .map(c => { + try { + // TODO: Remove the code in the try block with next major version release of CDK. + // It's here to be backwards compatible, i.e., prevent LogicalIds to change. + return stack.resolve(c); + } catch (e) { + return Tokenization.resolve(c, { + scope: this, + resolver: new DefaultTokenResolver(new StringConcat()), + preparing: true, + }); + } + }) .forEach(c => md5.update(JSON.stringify(c))); this.overrideLogicalId(this.originalLogicalId + md5.digest("hex")); } - super.prepare(); } } diff --git a/packages/@aws-cdk/aws-apigateway/lib/integrations/lambda.ts b/packages/@aws-cdk/aws-apigateway/lib/integrations/lambda.ts index 1aab6b38bedac..a2df9ef963358 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/integrations/lambda.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/integrations/lambda.ts @@ -1,5 +1,6 @@ import iam = require('@aws-cdk/aws-iam'); import lambda = require('@aws-cdk/aws-lambda'); +import cdk = require('@aws-cdk/core'); import { IntegrationOptions } from '../integration'; import { Method } from '../method'; import { AwsIntegration } from './aws'; @@ -60,14 +61,14 @@ export class LambdaIntegration extends AwsIntegration { this.handler.addPermission(`ApiPermission.${desc}`, { principal, sourceArn: method.methodArn, - }); + }, method.node.scope as cdk.Construct); // add permission to invoke from the console if (this.enableTest) { this.handler.addPermission(`ApiPermission.Test.${desc}`, { principal, sourceArn: method.testMethodArn - }); + }, method.node.scope as cdk.Construct); } } } diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.books.expected.json b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.books.expected.json index 30a9f8c4a180c..162692a9caf01 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.books.expected.json +++ b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.books.expected.json @@ -50,7 +50,7 @@ "BooksHandlerServiceRole5B6A8847" ] }, - "BooksHandlerApiPermissionrestapibooksexamplebooksapi4538F335GETbooks727D645E": { + "booksapibooksApiPermissionrestapibooksexamplebooksapi4538F335GETbooks9CD1395A": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", @@ -91,7 +91,7 @@ } } }, - "BooksHandlerApiPermissionTestrestapibooksexamplebooksapi4538F335GETbooksCC375808": { + "booksapibooksApiPermissionTestrestapibooksexamplebooksapi4538F335GETbooks8207720B": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", @@ -128,7 +128,7 @@ } } }, - "BooksHandlerApiPermissionrestapibooksexamplebooksapi4538F335POSTbooksFDED8A87": { + "booksapibooksApiPermissionrestapibooksexamplebooksapi4538F335POSTbooks4B64D4A2": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", @@ -169,7 +169,7 @@ } } }, - "BooksHandlerApiPermissionTestrestapibooksexamplebooksapi4538F335POSTbooks4667899F": { + "booksapibooksApiPermissionTestrestapibooksexamplebooksapi4538F335POSTbooks99DD50F5": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", @@ -256,7 +256,7 @@ "BookHandlerServiceRole894768AD" ] }, - "BookHandlerApiPermissionrestapibooksexamplebooksapi4538F335GETbooksbookidA10D3CE2": { + "booksapibooksbookidApiPermissionrestapibooksexamplebooksapi4538F335GETbooksbookidEC2C8EF7": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", @@ -297,7 +297,7 @@ } } }, - "BookHandlerApiPermissionTestrestapibooksexamplebooksapi4538F335GETbooksbookidAB5191B6": { + "booksapibooksbookidApiPermissionTestrestapibooksexamplebooksapi4538F335GETbooksbookid91952F8D": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", @@ -334,7 +334,7 @@ } } }, - "BookHandlerApiPermissionrestapibooksexamplebooksapi4538F335DELETEbooksbookidB3A85313": { + "booksapibooksbookidApiPermissionrestapibooksexamplebooksapi4538F335DELETEbooksbookid2A4ABB5E": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", @@ -375,7 +375,7 @@ } } }, - "BookHandlerApiPermissionTestrestapibooksexamplebooksapi4538F335DELETEbooksbookid9308C830": { + "booksapibooksbookidApiPermissionTestrestapibooksexamplebooksapi4538F335DELETEbooksbookid4D2C5A2E": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", @@ -462,7 +462,7 @@ "HelloServiceRole1E55EA16" ] }, - "HelloApiPermissionrestapibooksexamplebooksapi4538F335ANYE385693C": { + "booksapiApiPermissionrestapibooksexamplebooksapi4538F335ANY5C694FC0": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", @@ -503,7 +503,7 @@ } } }, - "HelloApiPermissionTestrestapibooksexamplebooksapi4538F335ANY46B0DA7B": { + "booksapiApiPermissionTestrestapibooksexamplebooksapi4538F335ANY308363F4": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.expected.json b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.expected.json index d8781b60a8f8c..ec2956baadf42 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.expected.json +++ b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.expected.json @@ -430,7 +430,7 @@ "MyHandlerServiceRoleFFA06653" ] }, - "MyHandlerApiPermissiontestapigatewayrestapimyapi1AE401C4GETv1toys00F704BC": { + "myapiv1toysApiPermissiontestapigatewayrestapimyapi1AE401C4GETv1toys4996AB00": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", @@ -471,7 +471,7 @@ } } }, - "MyHandlerApiPermissionTesttestapigatewayrestapimyapi1AE401C4GETv1toysDBCC8082": { + "myapiv1toysApiPermissionTesttestapigatewayrestapimyapi1AE401C4GETv1toys235E617F": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", @@ -508,7 +508,7 @@ } } }, - "MyHandlerApiPermissiontestapigatewayrestapimyapi1AE401C4GETv1books96EB3DB8": { + "myapiv1booksApiPermissiontestapigatewayrestapimyapi1AE401C4GETv1books569CDE64": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", @@ -549,7 +549,7 @@ } } }, - "MyHandlerApiPermissionTesttestapigatewayrestapimyapi1AE401C4GETv1books906B3BB6": { + "myapiv1booksApiPermissionTesttestapigatewayrestapimyapi1AE401C4GETv1books83C4BA55": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", @@ -586,7 +586,7 @@ } } }, - "MyHandlerApiPermissiontestapigatewayrestapimyapi1AE401C4POSTv1booksA48C273B": { + "myapiv1booksApiPermissiontestapigatewayrestapimyapi1AE401C4POSTv1books67C7577A": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", @@ -627,7 +627,7 @@ } } }, - "MyHandlerApiPermissionTesttestapigatewayrestapimyapi1AE401C4POSTv1booksA566985D": { + "myapiv1booksApiPermissionTesttestapigatewayrestapimyapi1AE401C4POSTv1booksE8FF0DE5": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multistack.expected.json b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multistack.expected.json new file mode 100644 index 0000000000000..86685a1d7c778 --- /dev/null +++ b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multistack.expected.json @@ -0,0 +1,260 @@ +[ + { + "Resources": { + "firstLambdaServiceRoleB6408C31": { + "Type":"AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action":"sts:AssumeRole", + "Effect":"Allow", + "Principal": {"Service":"lambda.amazonaws.com"} + } + ], + "Version":"2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + {"Ref":"AWS::Partition"}, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "firstLambda395F9ADE": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { "Ref":"firstLambdaCodeS3Bucket9779DE4A" }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { "Fn::Split": [ "||", { "Ref":"firstLambdaCodeS3VersionKey694A53D3" } ] } + ] + }, + { + "Fn::Select": [ + 1, + { "Fn::Split": [ "||", { "Ref": "firstLambdaCodeS3VersionKey694A53D3" } ] } + ] + } + ] + ] + } + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ "firstLambdaServiceRoleB6408C31", "Arn" ] + }, + "Runtime": "nodejs10.x", + "FunctionName": "FirstLambda" + }, + "DependsOn": [ "firstLambdaServiceRoleB6408C31" ] + } + }, + "Parameters": { + "firstLambdaCodeS3Bucket9779DE4A": { "Type": "String", "Description": "S3 bucket for asset \"FirstStack/firstLambda/Code\"" }, + "firstLambdaCodeS3VersionKey694A53D3": { "Type": "String", "Description": "S3 key for asset version \"FirstStack/firstLambda/Code\"" }, + "firstLambdaCodeArtifactHashBD56E0CA": { "Type": "String", "Description": "Artifact hash for asset \"FirstStack/firstLambda/Code\"" } + }, + "Outputs": { + "ExportsOutputFnGetAttfirstLambda395F9ADEArn1A9B3BC3": { + "Value": { "Fn::GetAtt": [ "firstLambda395F9ADE", "Arn" ] }, + "Export": { "Name": "FirstStack:ExportsOutputFnGetAttfirstLambda395F9ADEArn1A9B3BC3" } + } + } + }, + { + "Resources": { + "BooksApi60AC975F": { + "Type":"AWS::ApiGateway::RestApi", + "Properties": { "Name":"SecondRestAPI" } + }, + "BooksApiDeployment86CA39AF794fe41ee968b295eca9a15ee72a4a7c": { + "Type":"AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { "Ref": "BooksApi60AC975F" }, + "Description": "Automatically created by the RestApi construct" }, + "DependsOn": [ + "BooksApiANY0C4EABE3", + "BooksApibooksGET6066BF7E", + "BooksApibooks1F745538" + ] + }, + "BooksApiDeploymentStageprod0693B760": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "RestApiId": { "Ref": "BooksApi60AC975F" }, + "DeploymentId": { "Ref": "BooksApiDeployment86CA39AF794fe41ee968b295eca9a15ee72a4a7c" }, + "StageName": "prod" + } + }, + "BooksApiCloudWatchRoleB120ADBA": { + "Type":"AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument":{ + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": {"Service":"apigateway.amazonaws.com"} + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { "Ref": "AWS::Partition" }, + ":iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs" + ] + ] + } + ] + } + }, + "BooksApiAccount9C44AF8E": { + "Type": "AWS::ApiGateway::Account", + "Properties": { + "CloudWatchRoleArn": { + "Fn::GetAtt": [ "BooksApiCloudWatchRoleB120ADBA", "Arn" ] + } + }, + "DependsOn":[ "BooksApi60AC975F" ] + }, + "BooksApiANY0C4EABE3": { + "Type": "AWS::ApiGateway::Method", + "Properties": { + "HttpMethod": "ANY", + "ResourceId": { + "Fn::GetAtt": [ "BooksApi60AC975F","RootResourceId" ] + }, + "RestApiId": { "Ref":"BooksApi60AC975F" }, + "AuthorizationType": "NONE", + "Integration": { "Type": "MOCK" } + } + }, + "BooksApibooks1F745538": { + "Type": "AWS::ApiGateway::Resource", + "Properties": { + "ParentId": { + "Fn::GetAtt":[ "BooksApi60AC975F", "RootResourceId" ] + }, + "PathPart": "books", + "RestApiId": { "Ref": "BooksApi60AC975F" } + } + }, + "BooksApibooksGET6066BF7E": { + "Type": "AWS::ApiGateway::Method", + "Properties": { + "HttpMethod": "GET", + "ResourceId": { "Ref": "BooksApibooks1F745538" }, + "RestApiId": { "Ref": "BooksApi60AC975F" }, + "AuthorizationType": "NONE", + "Integration": { + "IntegrationHttpMethod": "POST", + "Type": "AWS_PROXY", + "Uri": { + "Fn::Join": [ + "", + [ + "arn:", + { "Ref":"AWS::Partition" }, + ":apigateway:", + { "Ref": "AWS::Region" }, + ":lambda:path/2015-03-31/functions/", + { "Fn::ImportValue": "FirstStack:ExportsOutputFnGetAttfirstLambda395F9ADEArn1A9B3BC3" }, + "/invocations" + ] + ] + } + } + } + }, + "BooksApibooksApiPermissionSecondStackBooksApi2660DEC5GETbooks3E9C9389": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Fn::ImportValue": "FirstStack:ExportsOutputFnGetAttfirstLambda395F9ADEArn1A9B3BC3" }, + "Principal":"apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { "Ref": "AWS::Partition" }, + ":execute-api:", + { "Ref": "AWS::Region" }, + ":", + { "Ref": "AWS::AccountId" }, + ":", + { "Ref": "BooksApi60AC975F" }, + "/", + { "Ref": "BooksApiDeploymentStageprod0693B760" }, + "/GET/books" + ] + ] + } + } + }, + "BooksApibooksApiPermissionTestSecondStackBooksApi2660DEC5GETbooks40D42DF6": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Fn::ImportValue": "FirstStack:ExportsOutputFnGetAttfirstLambda395F9ADEArn1A9B3BC3" }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { "Ref": "AWS::Partition" }, + ":execute-api:", + { "Ref": "AWS::Region" }, + ":", + { "Ref": "AWS::AccountId" }, + ":", + { "Ref": "BooksApi60AC975F" }, + "/test-invoke-stage/GET/books" + ] + ] + } + } + } + }, + "Outputs": { + "BooksApiEndpointF2AA70A2": { + "Value": { + "Fn::Join": [ + "", + [ + "https://", + { "Ref": "BooksApi60AC975F" }, + ".execute-api.", + { "Ref": "AWS::Region" }, + ".", + { "Ref": "AWS::URLSuffix" }, + "/", + { "Ref": "BooksApiDeploymentStageprod0693B760" }, + "/" + ] + ] + } + } + } + } +] \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multistack.ts b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multistack.ts new file mode 100644 index 0000000000000..89359c80fbc2e --- /dev/null +++ b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multistack.ts @@ -0,0 +1,43 @@ +/// !cdk-integ * + +import lambda = require('@aws-cdk/aws-lambda'); +import cdk = require('@aws-cdk/core'); +import apig = require('../lib'); + +class FirstStack extends cdk.Stack { + public readonly firstLambda: lambda.Function; + + constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + this.firstLambda = new lambda.Function(this, 'firstLambda', { + functionName: 'FirstLambda', + code: lambda.Code.asset('/Users/nija/workplace/cdk/hello-cdk/resources'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_10_X, + }); + } +} + +interface SecondStackProps extends cdk.StackProps { + readonly lambda: lambda.Function; +} + +class SecondStack extends cdk.Stack { + constructor(scope: cdk.Construct, id: string, props: SecondStackProps) { + super(scope, id, props); + + const api = new apig.RestApi(this, 'BooksApi', { + restApiName: 'SecondRestAPI' + }); + api.root.addMethod('ANY'); + const booksApi = api.root.addResource('books'); + const lambdaIntegration = new apig.LambdaIntegration(props.lambda); + booksApi.addMethod('GET', lambdaIntegration); + } +} + +const app = new cdk.App(); +const first = new FirstStack(app, 'FirstStack'); +new SecondStack(app, 'SecondStack', { lambda: first.firstLambda }); +app.synth(); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multiuse.expected.json b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multiuse.expected.json index 74179f434ccde..c484eb2020c6f 100644 --- a/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multiuse.expected.json +++ b/packages/@aws-cdk/aws-apigateway/test/integ.restapi.multiuse.expected.json @@ -50,7 +50,7 @@ "HelloServiceRole1E55EA16" ] }, - "HelloApiPermissionrestapimultiuseexamplehelloapi9FD0148DGEThello305E0522": { + "helloapihelloApiPermissionrestapimultiuseexamplehelloapi9FD0148DGEThello9C440B04": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", @@ -91,7 +91,7 @@ } } }, - "HelloApiPermissionTestrestapimultiuseexamplehelloapi9FD0148DGEThello8476E396": { + "helloapihelloApiPermissionTestrestapimultiuseexamplehelloapi9FD0148DGEThello7F98E05F": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", @@ -128,7 +128,7 @@ } } }, - "HelloApiPermissionrestapimultiuseexamplesecondapi5CB05B89GEThello268B558F": { + "secondapihelloApiPermissionrestapimultiuseexamplesecondapi5CB05B89GEThello3B7CB175": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", @@ -169,7 +169,7 @@ } } }, - "HelloApiPermissionTestrestapimultiuseexamplesecondapi5CB05B89GEThelloFF28C5F8": { + "secondapihelloApiPermissionTestrestapimultiuseexamplesecondapi5CB05B89GEThelloE0D6B261": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", diff --git a/packages/@aws-cdk/aws-lambda/lib/function-base.ts b/packages/@aws-cdk/aws-lambda/lib/function-base.ts index 984e9588efdc6..57d522f5f2104 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function-base.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function-base.ts @@ -1,14 +1,14 @@ import cloudwatch = require('@aws-cdk/aws-cloudwatch'); import ec2 = require('@aws-cdk/aws-ec2'); import iam = require('@aws-cdk/aws-iam'); -import { ConstructNode, IResource, Resource } from '@aws-cdk/core'; +import cdk = require('@aws-cdk/core'); import { IEventSource } from './event-source'; import { EventSourceMapping, EventSourceMappingOptions } from './event-source-mapping'; import { IVersion } from './lambda-version'; import { CfnPermission } from './lambda.generated'; import { Permission } from './permission'; -export interface IFunction extends IResource, ec2.IConnectable, iam.IGrantable { +export interface IFunction extends cdk.IResource, ec2.IConnectable, iam.IGrantable { /** * The name of the function. @@ -44,7 +44,7 @@ export interface IFunction extends IResource, ec2.IConnectable, iam.IGrantable { /** * The construct node where permissions are attached. */ - readonly permissionsNode: ConstructNode; + readonly permissionsNode: cdk.ConstructNode; /** * Adds an event source that maps to this AWS Lambda function. @@ -56,9 +56,14 @@ export interface IFunction extends IResource, ec2.IConnectable, iam.IGrantable { /** * Adds a permission to the Lambda resource policy. * @param id The id ƒor the permission construct + * @param permission The permission to grant to this Lambda function. @see Permission for details. + * @param scope An optional scope to which this permission should be attached. */ - addPermission(id: string, permission: Permission): void; + addPermission(id: string, permission: Permission, scope?: cdk.Construct): void; + /** + * Adds a statement to the IAM role assumed by the instance. + */ addToRolePolicy(statement: iam.PolicyStatement): void; /** @@ -132,7 +137,7 @@ export interface FunctionAttributes { readonly securityGroup?: ec2.ISecurityGroup; } -export abstract class FunctionBase extends Resource implements IFunction { +export abstract class FunctionBase extends cdk.Resource implements IFunction { /** * The principal this Lambda Function is running as */ @@ -158,7 +163,7 @@ export abstract class FunctionBase extends Resource implements IFunction { /** * The construct node where permissions are attached. */ - public abstract readonly permissionsNode: ConstructNode; + public abstract readonly permissionsNode: cdk.ConstructNode; /** * Whether the addPermission() call adds any permissions @@ -178,8 +183,10 @@ export abstract class FunctionBase extends Resource implements IFunction { /** * Adds a permission to the Lambda resource policy. * @param id The id ƒor the permission construct + * @param permission The permission to grant to this Lambda function. @see Permission for details. + * @param scope An optional scope to which this permission should be attached. By default, this will be the function handler. */ - public addPermission(id: string, permission: Permission) { + public addPermission(id: string, permission: Permission, scope?: cdk.Construct) { if (!this.canCreatePermissions) { // FIXME: Report metadata return; @@ -187,8 +194,9 @@ export abstract class FunctionBase extends Resource implements IFunction { const principal = this.parsePermissionPrincipal(permission.principal); const action = permission.action || 'lambda:InvokeFunction'; + scope = scope || this; - new CfnPermission(this, id, { + new CfnPermission(scope, id, { action, principal, functionName: this.functionArn, @@ -198,6 +206,9 @@ export abstract class FunctionBase extends Resource implements IFunction { }); } + /** + * Adds a statement to the IAM role assumed by the instance. + */ public addToRolePolicy(statement: iam.PolicyStatement) { if (!this.role) { return; diff --git a/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts b/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts index a86c5e937270b..dadd0249544f5 100644 --- a/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts +++ b/packages/@aws-cdk/aws-lambda/lib/singleton-lambda.ts @@ -60,8 +60,8 @@ export class SingletonFunction extends FunctionBase { this.canCreatePermissions = true; // Doesn't matter, addPermission is overriden anyway } - public addPermission(name: string, permission: Permission) { - return this.lambdaFunction.addPermission(name, permission); + public addPermission(name: string, permission: Permission, scope?: cdk.Construct) { + return this.lambdaFunction.addPermission(name, permission, scope); } private ensureLambda(props: SingletonFunctionProps): IFunction { diff --git a/packages/@aws-cdk/core/lib/token.ts b/packages/@aws-cdk/core/lib/token.ts index c57ace245b273..3f21bf22368a4 100644 --- a/packages/@aws-cdk/core/lib/token.ts +++ b/packages/@aws-cdk/core/lib/token.ts @@ -112,8 +112,9 @@ export class Tokenization { */ public static resolve(obj: any, options: ResolveOptions): any { return resolve(obj, { - ...options, - preparing: false + scope: options.scope, + resolver: options.resolver, + preparing: (options.preparing !== undefined ? options.preparing : false) }); } @@ -150,6 +151,11 @@ export interface ResolveOptions { * The resolver to apply to any resolvable tokens found */ readonly resolver: ITokenResolver; + + /** + * Whether the resolution is being executed during the prepare phase or not. + */ + readonly preparing?: boolean; } /**