From 26813bce0b8dd345fd31229933dbdd4b3ac56967 Mon Sep 17 00:00:00 2001 From: Tsimpitas Dimitris Date: Sun, 2 Jan 2022 15:42:22 +0200 Subject: [PATCH 1/5] Add forceDeploy property in Cognito User Pools --- .../package/compile/events/cognitoUserPool.js | 5 +- .../compile/events/cognitoUserPool.test.js | 74 +++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/lib/plugins/aws/package/compile/events/cognitoUserPool.js b/lib/plugins/aws/package/compile/events/cognitoUserPool.js index 34bafdace0a..55324c6f352 100644 --- a/lib/plugins/aws/package/compile/events/cognitoUserPool.js +++ b/lib/plugins/aws/package/compile/events/cognitoUserPool.js @@ -39,6 +39,7 @@ class AwsCompileCognitoUserPoolEvents { pool: { type: 'string', maxLength: 128, pattern: '^[\\w\\s+=,.@-]+$' }, trigger: { enum: validTriggerSources }, existing: { type: 'boolean' }, + forceDeploy: { type: 'boolean' }, }, required: ['pool', 'trigger'], additionalProperties: false, @@ -141,7 +142,7 @@ class AwsCompileCognitoUserPoolEvents { functionObj.events.forEach((event) => { if (event.cognitoUserPool && event.cognitoUserPool.existing) { numEventsForFunc++; - const { pool, trigger } = event.cognitoUserPool; + const { pool, trigger, forceDeploy } = event.cognitoUserPool; usesExistingCognitoUserPool = funcUsesExistingCognitoUserPool = true; if (!currentPoolName) { @@ -171,6 +172,7 @@ class AwsCompileCognitoUserPoolEvents { } let customCognitoUserPoolResource; + const forceDeployProperty = forceDeploy ? Math.random() * 10 : 0; if (numEventsForFunc === 1) { customCognitoUserPoolResource = { [customPoolResourceLogicalId]: { @@ -188,6 +190,7 @@ class AwsCompileCognitoUserPoolEvents { Trigger: trigger, }, ], + ForceDeploy: forceDeployProperty, }, }, }; diff --git a/test/unit/lib/plugins/aws/package/compile/events/cognitoUserPool.test.js b/test/unit/lib/plugins/aws/package/compile/events/cognitoUserPool.test.js index a920b1dfca0..5533905b4c2 100644 --- a/test/unit/lib/plugins/aws/package/compile/events/cognitoUserPool.test.js +++ b/test/unit/lib/plugins/aws/package/compile/events/cognitoUserPool.test.js @@ -339,6 +339,77 @@ describe('AwsCompileCognitoUserPoolEvents', () => { Trigger: 'CustomMessage', }, ], + ForceDeploy: 0, + }, + }); + }); + }); + + it('should create the necessary resources for the most minimal configuration with forceDeploy', () => { + awsCompileCognitoUserPoolEvents.serverless.service.functions = { + first: { + name: 'first', + events: [ + { + cognitoUserPool: { + pool: 'existing-cognito-user-pool', + trigger: 'CustomMessage', + existing: true, + forceDeploy: true, + }, + }, + ], + }, + }; + + return expect( + awsCompileCognitoUserPoolEvents.existingCognitoUserPools() + ).to.be.fulfilled.then(() => { + const { Resources } = + awsCompileCognitoUserPoolEvents.serverless.service.provider + .compiledCloudFormationTemplate; + + expect(addCustomResourceToServiceStub).to.have.been.calledOnce; + expect(addCustomResourceToServiceStub.args[0][1]).to.equal('cognitoUserPool'); + expect(addCustomResourceToServiceStub.args[0][2]).to.deep.equal([ + { + Action: [ + 'cognito-idp:ListUserPools', + 'cognito-idp:DescribeUserPool', + 'cognito-idp:UpdateUserPool', + ], + Effect: 'Allow', + Resource: '*', + }, + { + Action: ['lambda:AddPermission', 'lambda:RemovePermission'], + Effect: 'Allow', + Resource: { 'Fn::Sub': 'arn:${AWS::Partition}:lambda:*:*:function:first' }, + }, + { + Effect: 'Allow', + Resource: { + 'Fn::Sub': 'arn:${AWS::Partition}:iam::*:role/*', + }, + Action: ['iam:PassRole'], + }, + ]); + expect(Resources.FirstCustomCognitoUserPool1).to.not.deep.equal({ + Type: 'Custom::CognitoUserPool', + Version: 1, + DependsOn: ['FirstLambdaFunction', 'CustomDashresourceDashexistingDashcupLambdaFunction'], + Properties: { + ServiceToken: { + 'Fn::GetAtt': ['CustomDashresourceDashexistingDashcupLambdaFunction', 'Arn'], + }, + FunctionName: 'first', + UserPoolName: 'existing-cognito-user-pool', + UserPoolConfigs: [ + { + Trigger: 'CustomMessage', + }, + ], + ForceDeploy: 0, }, }); }); @@ -427,6 +498,7 @@ describe('AwsCompileCognitoUserPoolEvents', () => { Trigger: 'DefineAuthChallenge', }, ], + ForceDeploy: 0, }, }); }); @@ -556,6 +628,7 @@ describe('AwsCompileCognitoUserPoolEvents', () => { Trigger: 'DefineAuthChallenge', }, ], + ForceDeploy: 0, }, }); expect(Resources.SecondCustomCognitoUserPool1).to.deep.equal({ @@ -583,6 +656,7 @@ describe('AwsCompileCognitoUserPoolEvents', () => { Trigger: 'PostAuthentication', }, ], + ForceDeploy: 0, }, }); }); From e5f444fe95194ae4100dbe69f3e7a5d3ba220a84 Mon Sep 17 00:00:00 2001 From: Tsimpitas Dimitris Date: Mon, 3 Jan 2022 17:13:49 +0200 Subject: [PATCH 2/5] Change from Math.random() to Date.now() --- .../aws/package/compile/events/cognitoUserPool.js | 2 +- .../aws/package/compile/events/cognitoUserPool.test.js | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/plugins/aws/package/compile/events/cognitoUserPool.js b/lib/plugins/aws/package/compile/events/cognitoUserPool.js index 55324c6f352..2b08bf74d67 100644 --- a/lib/plugins/aws/package/compile/events/cognitoUserPool.js +++ b/lib/plugins/aws/package/compile/events/cognitoUserPool.js @@ -172,7 +172,7 @@ class AwsCompileCognitoUserPoolEvents { } let customCognitoUserPoolResource; - const forceDeployProperty = forceDeploy ? Math.random() * 10 : 0; + const forceDeployProperty = forceDeploy ? Date.now() : undefined; if (numEventsForFunc === 1) { customCognitoUserPoolResource = { [customPoolResourceLogicalId]: { diff --git a/test/unit/lib/plugins/aws/package/compile/events/cognitoUserPool.test.js b/test/unit/lib/plugins/aws/package/compile/events/cognitoUserPool.test.js index 5533905b4c2..a7a9a886eec 100644 --- a/test/unit/lib/plugins/aws/package/compile/events/cognitoUserPool.test.js +++ b/test/unit/lib/plugins/aws/package/compile/events/cognitoUserPool.test.js @@ -339,7 +339,7 @@ describe('AwsCompileCognitoUserPoolEvents', () => { Trigger: 'CustomMessage', }, ], - ForceDeploy: 0, + ForceDeploy: undefined, }, }); }); @@ -409,7 +409,7 @@ describe('AwsCompileCognitoUserPoolEvents', () => { Trigger: 'CustomMessage', }, ], - ForceDeploy: 0, + ForceDeploy: undefined, }, }); }); @@ -498,7 +498,7 @@ describe('AwsCompileCognitoUserPoolEvents', () => { Trigger: 'DefineAuthChallenge', }, ], - ForceDeploy: 0, + ForceDeploy: undefined, }, }); }); @@ -628,7 +628,7 @@ describe('AwsCompileCognitoUserPoolEvents', () => { Trigger: 'DefineAuthChallenge', }, ], - ForceDeploy: 0, + ForceDeploy: undefined, }, }); expect(Resources.SecondCustomCognitoUserPool1).to.deep.equal({ @@ -656,7 +656,7 @@ describe('AwsCompileCognitoUserPoolEvents', () => { Trigger: 'PostAuthentication', }, ], - ForceDeploy: 0, + ForceDeploy: undefined, }, }); }); From b682c3771a333f0e645a451bfdce599124a61ed9 Mon Sep 17 00:00:00 2001 From: Tsimpitas Dimitris Date: Tue, 4 Jan 2022 17:00:02 +0200 Subject: [PATCH 3/5] Change test implementation to use runServerless --- .../compile/events/cognitoUserPool.test.js | 88 +++++-------------- 1 file changed, 24 insertions(+), 64 deletions(-) diff --git a/test/unit/lib/plugins/aws/package/compile/events/cognitoUserPool.test.js b/test/unit/lib/plugins/aws/package/compile/events/cognitoUserPool.test.js index a7a9a886eec..917f914991e 100644 --- a/test/unit/lib/plugins/aws/package/compile/events/cognitoUserPool.test.js +++ b/test/unit/lib/plugins/aws/package/compile/events/cognitoUserPool.test.js @@ -7,6 +7,7 @@ const chai = require('chai'); const proxyquire = require('proxyquire').noCallThru(); const AwsProvider = require('../../../../../../../../lib/plugins/aws/provider'); const Serverless = require('../../../../../../../../lib/Serverless'); +const runServerless = require('../../../../../../../utils/run-serverless'); const { expect } = chai; chai.use(require('sinon-chai')); @@ -345,74 +346,33 @@ describe('AwsCompileCognitoUserPoolEvents', () => { }); }); - it('should create the necessary resources for the most minimal configuration with forceDeploy', () => { - awsCompileCognitoUserPoolEvents.serverless.service.functions = { - first: { - name: 'first', - events: [ - { - cognitoUserPool: { - pool: 'existing-cognito-user-pool', - trigger: 'CustomMessage', - existing: true, - forceDeploy: true, - }, + it('should create the necessary resources for the most minimal configuration with forceDeploy', async () => { + const result = await runServerless({ + fixture: 'cognitoUserPool', + configExt: { + functions: { + existingSimple: { + events: [ + { + cognitoUserPool: { + forceDeploy: true, + }, + }, + ], }, - ], + }, }, - }; + command: 'package', + }); - return expect( - awsCompileCognitoUserPoolEvents.existingCognitoUserPools() - ).to.be.fulfilled.then(() => { - const { Resources } = - awsCompileCognitoUserPoolEvents.serverless.service.provider - .compiledCloudFormationTemplate; + const { Resources } = result.cfTemplate; + const { awsNaming } = result; - expect(addCustomResourceToServiceStub).to.have.been.calledOnce; - expect(addCustomResourceToServiceStub.args[0][1]).to.equal('cognitoUserPool'); - expect(addCustomResourceToServiceStub.args[0][2]).to.deep.equal([ - { - Action: [ - 'cognito-idp:ListUserPools', - 'cognito-idp:DescribeUserPool', - 'cognito-idp:UpdateUserPool', - ], - Effect: 'Allow', - Resource: '*', - }, - { - Action: ['lambda:AddPermission', 'lambda:RemovePermission'], - Effect: 'Allow', - Resource: { 'Fn::Sub': 'arn:${AWS::Partition}:lambda:*:*:function:first' }, - }, - { - Effect: 'Allow', - Resource: { - 'Fn::Sub': 'arn:${AWS::Partition}:iam::*:role/*', - }, - Action: ['iam:PassRole'], - }, - ]); - expect(Resources.FirstCustomCognitoUserPool1).to.not.deep.equal({ - Type: 'Custom::CognitoUserPool', - Version: 1, - DependsOn: ['FirstLambdaFunction', 'CustomDashresourceDashexistingDashcupLambdaFunction'], - Properties: { - ServiceToken: { - 'Fn::GetAtt': ['CustomDashresourceDashexistingDashcupLambdaFunction', 'Arn'], - }, - FunctionName: 'first', - UserPoolName: 'existing-cognito-user-pool', - UserPoolConfigs: [ - { - Trigger: 'CustomMessage', - }, - ], - ForceDeploy: undefined, - }, - }); - }); + const customResource = + Resources[awsNaming.getCustomResourceCognitoUserPoolResourceLogicalId('existingSimple')]; + + expect(customResource.Properties.ForceDeploy).to.not.deep.equal(undefined); + expect(customResource.Properties.ForceDeploy).to.be.below(Date.now()).and.to.be.above(0); }); it('should create the necessary resources for a service using multiple event definitions', () => { From e85d1ce6b493929b20ab2057299127e133c9669e Mon Sep 17 00:00:00 2001 From: Tsimpitas Dimitris Date: Tue, 4 Jan 2022 17:04:21 +0200 Subject: [PATCH 4/5] Update documentation --- docs/providers/aws/events/cognito-user-pool.md | 16 ++++++++++++++++ docs/providers/aws/guide/serverless.yml.md | 1 + 2 files changed, 17 insertions(+) diff --git a/docs/providers/aws/events/cognito-user-pool.md b/docs/providers/aws/events/cognito-user-pool.md index a8b9129f9a5..5fb141949b7 100644 --- a/docs/providers/aws/events/cognito-user-pool.md +++ b/docs/providers/aws/events/cognito-user-pool.md @@ -134,5 +134,21 @@ resources: Type: AWS::Cognito::UserPool ``` +## Forcing deploying of triggers + +A Cognito User Pool with triggers attached may not be correctly updated by AWS Cloudformation on subsequent deployments. To circumvent this issue you can use the `forceDeploy` flag which will try to force Cloudformation to update the triggers no matter what. This flag has to be used in conjuction with the `existing: true` flag. + +```yml +functions: + preSignUp: + handler: preSignUp.handler + events: + - cognitoUserPool: + pool: MyUserPool1 + trigger: PreSignUp + existing: true + forceDeploy: true +``` + [aws-triggers-guide]: http://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html [aws-triggers-list]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cognito-userpool-lambdaconfig.html diff --git a/docs/providers/aws/guide/serverless.yml.md b/docs/providers/aws/guide/serverless.yml.md index 716971cae38..82064b7fe68 100644 --- a/docs/providers/aws/guide/serverless.yml.md +++ b/docs/providers/aws/guide/serverless.yml.md @@ -509,6 +509,7 @@ functions: pool: MyUserPool trigger: PreSignUp existing: true # optional, if you're referencing an existing User Pool + forceDeploy: true # optional, for forcing deployment of triggers on existing User Pools - alb: listenerArn: arn:aws:elasticloadbalancing:us-east-1:12345:listener/app/my-load-balancer/50dc6c495c0c9188/ priority: 1 From 4ad509c3b893a74e7d4cf517cc59e8f876572895 Mon Sep 17 00:00:00 2001 From: Tsimpitas Dimitris Date: Tue, 4 Jan 2022 18:36:13 +0200 Subject: [PATCH 5/5] Simplify test case --- .../aws/package/compile/events/cognitoUserPool.test.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/unit/lib/plugins/aws/package/compile/events/cognitoUserPool.test.js b/test/unit/lib/plugins/aws/package/compile/events/cognitoUserPool.test.js index 917f914991e..4ea548fe63c 100644 --- a/test/unit/lib/plugins/aws/package/compile/events/cognitoUserPool.test.js +++ b/test/unit/lib/plugins/aws/package/compile/events/cognitoUserPool.test.js @@ -346,7 +346,7 @@ describe('AwsCompileCognitoUserPoolEvents', () => { }); }); - it('should create the necessary resources for the most minimal configuration with forceDeploy', async () => { + it('should support `forceDeploy` setting', async () => { const result = await runServerless({ fixture: 'cognitoUserPool', configExt: { @@ -371,8 +371,7 @@ describe('AwsCompileCognitoUserPoolEvents', () => { const customResource = Resources[awsNaming.getCustomResourceCognitoUserPoolResourceLogicalId('existingSimple')]; - expect(customResource.Properties.ForceDeploy).to.not.deep.equal(undefined); - expect(customResource.Properties.ForceDeploy).to.be.below(Date.now()).and.to.be.above(0); + expect(typeof customResource.Properties.ForceDeploy).to.equal('number'); }); it('should create the necessary resources for a service using multiple event definitions', () => {