mirror of
https://github.com/serverless/serverless.git
synced 2026-01-25 15:07:39 +00:00
Merge pull request #6871 from serverless/support-cfn-role-in-custom-resources
Honor cfnRole in custom resources
This commit is contained in:
commit
8a8f1995d7
@ -86,78 +86,83 @@ function addCustomResourceToService(awsProvider, resourceName, iamRoleStatements
|
||||
const s3FileName = outputFilePath.split(path.sep).pop();
|
||||
const S3Key = `${s3Folder}/${s3FileName}`;
|
||||
|
||||
let customResourceRole = Resources[customResourcesRoleLogicalId];
|
||||
if (!customResourceRole) {
|
||||
customResourceRole = {
|
||||
Type: 'AWS::IAM::Role',
|
||||
Properties: {
|
||||
AssumeRolePolicyDocument: {
|
||||
Version: '2012-10-17',
|
||||
Statement: [
|
||||
{
|
||||
Effect: 'Allow',
|
||||
Principal: {
|
||||
Service: ['lambda.amazonaws.com'],
|
||||
},
|
||||
Action: ['sts:AssumeRole'],
|
||||
},
|
||||
],
|
||||
},
|
||||
Policies: [
|
||||
{
|
||||
PolicyName: {
|
||||
'Fn::Join': [
|
||||
'-',
|
||||
[
|
||||
awsProvider.getStage(),
|
||||
awsProvider.serverless.service.service,
|
||||
'custom-resources-lambda',
|
||||
],
|
||||
],
|
||||
},
|
||||
PolicyDocument: {
|
||||
Version: '2012-10-17',
|
||||
Statement: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
const cfnRoleArn = serverless.service.provider.cfnRole;
|
||||
|
||||
if (shouldWriteLogs) {
|
||||
const logGroupsPrefix = awsProvider.naming.getLogGroupName(funcPrefix);
|
||||
customResourceRole.Properties.Policies[0].PolicyDocument.Statement.push(
|
||||
{
|
||||
Effect: 'Allow',
|
||||
Action: ['logs:CreateLogStream'],
|
||||
Resource: [
|
||||
if (!cfnRoleArn) {
|
||||
let customResourceRole = Resources[customResourcesRoleLogicalId];
|
||||
if (!customResourceRole) {
|
||||
customResourceRole = {
|
||||
Type: 'AWS::IAM::Role',
|
||||
Properties: {
|
||||
AssumeRolePolicyDocument: {
|
||||
Version: '2012-10-17',
|
||||
Statement: [
|
||||
{
|
||||
Effect: 'Allow',
|
||||
Principal: {
|
||||
Service: ['lambda.amazonaws.com'],
|
||||
},
|
||||
Action: ['sts:AssumeRole'],
|
||||
},
|
||||
],
|
||||
},
|
||||
Policies: [
|
||||
{
|
||||
'Fn::Sub':
|
||||
'arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}' +
|
||||
`:log-group:${logGroupsPrefix}*:*`,
|
||||
PolicyName: {
|
||||
'Fn::Join': [
|
||||
'-',
|
||||
[
|
||||
awsProvider.getStage(),
|
||||
awsProvider.serverless.service.service,
|
||||
'custom-resources-lambda',
|
||||
],
|
||||
],
|
||||
},
|
||||
PolicyDocument: {
|
||||
Version: '2012-10-17',
|
||||
Statement: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
Effect: 'Allow',
|
||||
Action: ['logs:PutLogEvents'],
|
||||
Resource: [
|
||||
{
|
||||
'Fn::Sub':
|
||||
'arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}' +
|
||||
`:log-group:${logGroupsPrefix}*:*:*`,
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
};
|
||||
Resources[customResourcesRoleLogicalId] = customResourceRole;
|
||||
|
||||
if (shouldWriteLogs) {
|
||||
const logGroupsPrefix = awsProvider.naming.getLogGroupName(funcPrefix);
|
||||
customResourceRole.Properties.Policies[0].PolicyDocument.Statement.push(
|
||||
{
|
||||
Effect: 'Allow',
|
||||
Action: ['logs:CreateLogStream'],
|
||||
Resource: [
|
||||
{
|
||||
'Fn::Sub':
|
||||
'arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}' +
|
||||
`:log-group:${logGroupsPrefix}*:*`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
Effect: 'Allow',
|
||||
Action: ['logs:PutLogEvents'],
|
||||
Resource: [
|
||||
{
|
||||
'Fn::Sub':
|
||||
'arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}' +
|
||||
`:log-group:${logGroupsPrefix}*:*:*`,
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
const { Statement } = customResourceRole.Properties.Policies[0].PolicyDocument;
|
||||
iamRoleStatements.forEach(newStmt => {
|
||||
if (!Statement.find(existingStmt => existingStmt.Resource === newStmt.Resource)) {
|
||||
Statement.push(newStmt);
|
||||
}
|
||||
});
|
||||
}
|
||||
const { Statement } = customResourceRole.Properties.Policies[0].PolicyDocument;
|
||||
iamRoleStatements.forEach(newStmt => {
|
||||
if (!Statement.find(existingStmt => existingStmt.Resource === newStmt.Resource)) {
|
||||
Statement.push(newStmt);
|
||||
}
|
||||
});
|
||||
|
||||
const customResourceFunction = {
|
||||
Type: 'AWS::Lambda::Function',
|
||||
@ -169,19 +174,21 @@ function addCustomResourceToService(awsProvider, resourceName, iamRoleStatements
|
||||
FunctionName: absoluteFunctionName,
|
||||
Handler,
|
||||
MemorySize: 1024,
|
||||
Role: {
|
||||
'Fn::GetAtt': [customResourcesRoleLogicalId, 'Arn'],
|
||||
},
|
||||
Runtime: 'nodejs10.x',
|
||||
Timeout: 180,
|
||||
},
|
||||
DependsOn: [customResourcesRoleLogicalId],
|
||||
DependsOn: [],
|
||||
};
|
||||
Resources[customResourceFunctionLogicalId] = customResourceFunction;
|
||||
|
||||
Object.assign(Resources, {
|
||||
[customResourceFunctionLogicalId]: customResourceFunction,
|
||||
[customResourcesRoleLogicalId]: customResourceRole,
|
||||
});
|
||||
if (cfnRoleArn) {
|
||||
customResourceFunction.Properties.Role = cfnRoleArn;
|
||||
} else {
|
||||
customResourceFunction.Properties.Role = {
|
||||
'Fn::GetAtt': [customResourcesRoleLogicalId, 'Arn'],
|
||||
};
|
||||
customResourceFunction.DependsOn.push(customResourcesRoleLogicalId);
|
||||
}
|
||||
|
||||
if (shouldWriteLogs) {
|
||||
const customResourceLogGroupLogicalId = awsProvider.naming.getLogGroupLogicalId(
|
||||
|
||||
@ -221,6 +221,119 @@ describe('#addCustomResourceToService()', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('Should not setup new IAM role, when cfnRole is provided', () => {
|
||||
const cfnRoleArn = (serverless.service.provider.cfnRole =
|
||||
'arn:aws:iam::999999999999:role/some-role');
|
||||
return expect(
|
||||
BbPromise.all([
|
||||
// add the custom S3 resource
|
||||
addCustomResourceToService(provider, 's3', [
|
||||
...iamRoleStatements,
|
||||
{
|
||||
Effect: 'Allow',
|
||||
Resource: 'arn:aws:s3:::some-bucket',
|
||||
Action: ['s3:PutBucketNotification', 's3:GetBucketNotification'],
|
||||
},
|
||||
]),
|
||||
// add the custom Cognito User Pool resource
|
||||
addCustomResourceToService(provider, 'cognitoUserPool', [
|
||||
...iamRoleStatements,
|
||||
{
|
||||
Effect: 'Allow',
|
||||
Resource: '*',
|
||||
Action: [
|
||||
'cognito-idp:ListUserPools',
|
||||
'cognito-idp:DescribeUserPool',
|
||||
'cognito-idp:UpdateUserPool',
|
||||
],
|
||||
},
|
||||
]),
|
||||
// add the custom Event Bridge resource
|
||||
addCustomResourceToService(provider, 'eventBridge', [
|
||||
...iamRoleStatements,
|
||||
{
|
||||
Effect: 'Allow',
|
||||
Resource: 'arn:aws:events:*:*:rule/some-rule',
|
||||
Action: [
|
||||
'events:PutRule',
|
||||
'events:RemoveTargets',
|
||||
'events:PutTargets',
|
||||
'events:DeleteRule',
|
||||
],
|
||||
},
|
||||
{
|
||||
Action: ['events:CreateEventBus', 'events:DeleteEventBus'],
|
||||
Effect: 'Allow',
|
||||
Resource: 'arn:aws:events:*:*:event-bus/some-event-bus',
|
||||
},
|
||||
]),
|
||||
])
|
||||
).to.be.fulfilled.then(() => {
|
||||
const { Resources } = serverless.service.provider.compiledCloudFormationTemplate;
|
||||
const customResourcesZipFilePath = path.join(
|
||||
tmpDirPath,
|
||||
'.serverless',
|
||||
'custom-resources.zip'
|
||||
);
|
||||
|
||||
expect(execAsyncStub).to.have.callCount(3);
|
||||
expect(fs.existsSync(customResourcesZipFilePath)).to.equal(true);
|
||||
// S3 Lambda Function
|
||||
expect(Resources.CustomDashresourceDashexistingDashs3LambdaFunction).to.deep.equal({
|
||||
Type: 'AWS::Lambda::Function',
|
||||
Properties: {
|
||||
Code: {
|
||||
S3Bucket: { Ref: 'ServerlessDeploymentBucket' },
|
||||
S3Key: 'artifact-dir-name/custom-resources.zip',
|
||||
},
|
||||
FunctionName: `${serviceName}-dev-custom-resource-existing-s3`,
|
||||
Handler: 's3/handler.handler',
|
||||
MemorySize: 1024,
|
||||
Role: cfnRoleArn,
|
||||
Runtime: 'nodejs10.x',
|
||||
Timeout: 180,
|
||||
},
|
||||
DependsOn: [],
|
||||
});
|
||||
// Cognito User Pool Lambda Function
|
||||
expect(Resources.CustomDashresourceDashexistingDashcupLambdaFunction).to.deep.equal({
|
||||
Type: 'AWS::Lambda::Function',
|
||||
Properties: {
|
||||
Code: {
|
||||
S3Bucket: { Ref: 'ServerlessDeploymentBucket' },
|
||||
S3Key: 'artifact-dir-name/custom-resources.zip',
|
||||
},
|
||||
FunctionName: `${serviceName}-dev-custom-resource-existing-cup`,
|
||||
Handler: 'cognitoUserPool/handler.handler',
|
||||
MemorySize: 1024,
|
||||
Role: cfnRoleArn,
|
||||
Runtime: 'nodejs10.x',
|
||||
Timeout: 180,
|
||||
},
|
||||
DependsOn: [],
|
||||
});
|
||||
// Event Bridge Lambda Function
|
||||
expect(Resources.CustomDashresourceDasheventDashbridgeLambdaFunction).to.deep.equal({
|
||||
Type: 'AWS::Lambda::Function',
|
||||
Properties: {
|
||||
Code: {
|
||||
S3Bucket: { Ref: 'ServerlessDeploymentBucket' },
|
||||
S3Key: 'artifact-dir-name/custom-resources.zip',
|
||||
},
|
||||
FunctionName: `${serviceName}-dev-custom-resource-event-bridge`,
|
||||
Handler: 'eventBridge/handler.handler',
|
||||
MemorySize: 1024,
|
||||
Role: cfnRoleArn,
|
||||
Runtime: 'nodejs10.x',
|
||||
Timeout: 180,
|
||||
},
|
||||
DependsOn: [],
|
||||
});
|
||||
// Iam Role
|
||||
expect(Resources.IamRoleCustomResourcesLambdaExecution).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
it('should setup CloudWatch Logs when logs.frameworkLambda is true', () => {
|
||||
serverless.service.provider.logs = { frameworkLambda: true };
|
||||
return BbPromise.all([
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user