Amplify-cli: Cannot give postConfirmation trigger access to auth

Created on 16 Sep 2019  Â·  26Comments  Â·  Source: aws-amplify/amplify-cli

Describe the bug
I have created a post confirmation trigger using amplify update auth.

I would like to give the postConfirmation trigger access to the user pool in order to get user information and perform other operations that affect the user pool.

I then used amplify update function in order to give the post confirmation function access to the user pool.

However, an error occurs saying: "Error: Cannot add [functionName] due to a cyclic dependency" where [functionName] is the name of my function.

The postConfirmation cloudformation template file is updated with the appropriate permissions but the user pool id is not passed down as a parameter from nested-cloudformation-stack.yml

Desktop (please complete the following information):

  • OS: windows 10

Additional context
Invoking amplify update auth and then adding add-to-group functionality works fine but invoking amplify update function and then giving permissions to auth throws the error stated above.

I have tried this with @aws-amplify/cli 3.0.0 and 3.2.0, both result in the same error.

Thanks a lot!

enhancement pending-review platform

Most helpful comment

Hi @royalaid @paulsson ,

not sure if this is the correct approach? But basically my solution involves the following:

  • editing my existing post confirmation trigger lambda business logic to make use of cognito (figure 1)
  • editing the post confirmation trigger lambda's cloudformation template, adding the LambdaExecutionRole resource's name as an output (figure 2)
  • adding a custom resource which depends on the cognito user pool id value and the post confirmation trigger lambda's Lambda execution Role name output value and this custom resource will create the IAM policy to access the given cognito user pool (using the user pool id reference) and attach it to the lambda's role based on the LambdaExecutionRole name reference (figure 3)

Doing the above whilst reading this section of the docs on deploying custom resources made sense to me: https://aws-amplify.github.io/docs/cli-toolchain/quickstart#custom-cloudformation-stacks

figure 1:

const cognitoIdentityServiceProvider = new CognitoIdentityServiceProvider()

figure 2:

{
  ....,
  "Outputs": {
    ....,
    "LambdaExecutionRole": {
      "Value": {
        "Ref": "LambdaExecutionRole"        
      }
    }
  }
}

figure 3:
backend-config.json

{
  ...,
"cognitoLambdaTriggerPermissions": {
        "postConfirmationPermissions": {
            "service": "Cognito-Lambda-Trigger-Permissions",
            "providerPlugin": "awscloudformation",
            "dependsOn": [
                {
                    "category": "auth",
                    "resourceName": "customerAppCognito",
                    "attributes": [
                        "UserPoolId"
                    ]
                },
                {
                    "category": "function",
                    "resourceName": "customerAppCognitoPostConfirmation",
                    "attributes": [
                        "LambdaExecutionRole"
                    ]
                }
            ]
        }
    }
}

cognitoLambdaTriggerPeromissions/postConfirmationPermissions/template.json

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "Resource stack to apply cognito permissions to circular dependency lambda",
    "Parameters": {
        "env": {
            "Type": "String"
        },
        "authcustomerAppCognitoUserPoolId": {
            "Type": "String"        
        },
        "functioncustomerAppCognitoPostConfirmationLambdaExecutionRole": {
            "Type": "String"        
        }
    },
    "Conditions": {

    },
    "Resources": {
        "PostConfirmationCognitoResourcesPolicy": {
            "Type": "AWS::IAM::Policy",
            "Properties": {
                "PolicyName": "post-confirmation-cognito-execution-policy",
                "Roles": [
                    {
                        "Ref": "functioncustomerAppCognitoPostConfirmationLambdaExecutionRole"
                    }
                ],
                "PolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Action": [
                                "cognito-identity:Describe*",
                                "cognito-identity:Get*",
                                "cognito-identity:List*",
                                "cognito-idp:Describe*",
                                "cognito-idp:AdminGetDevice",
                                "cognito-idp:AdminGetUser",
                                "cognito-idp:AdminList*",
                                "cognito-idp:List*",
                                "cognito-sync:Describe*",
                                "cognito-sync:Get*",
                                "cognito-sync:List*",
                                "iam:ListOpenIdConnectProviders",
                                "iam:ListRoles",
                                "sns:ListPlatformApplications",
                                "cognito-idp:ForgotPassword",
                                "cognito-idp:UpdateAuthEventFeedback",
                                "cognito-idp:UpdateResourceServer",
                                "cognito-idp:UpdateUserPoolClient",
                                "cognito-idp:AdminUpdateUserAttributes",
                                "cognito-idp:UpdateUserAttributes",
                                "cognito-idp:UpdateUserPoolDomain",
                                "cognito-idp:UpdateIdentityProvider",
                                "cognito-idp:UpdateGroup",
                                "cognito-idp:AdminUpdateAuthEventFeedback",
                                "cognito-idp:UpdateDeviceStatus",
                                "cognito-idp:UpdateUserPool"
                            ],
                            "Resource": [
                                {
                                    "Fn::Join": [
                                        "",
                                        [
                                            "arn:aws:cognito-idp:",
                                            {
                                                "Ref": "AWS::Region"
                                            },
                                            ":",
                                            {
                                                "Ref": "AWS::AccountId"
                                            },
                                            ":userpool/",
                                            {
                                                "Ref": "authcustomerAppCognitoUserPoolId"
                                            }
                                        ]
                                    ]
                                }
                            ]
                        }
                    ]
                }
            }
        }
    },
    "Outputs": {

    }
}

cognitoLambdaTriggerPeromissions/postConfirmationPermissions/parameters.json

{
    "authcustomerAppCognitoUserPoolId": {  
       "Fn::GetAtt": [
          "customerAppCognito", 
          "Outputs.UserPoolId"
       ]
    },
    "functioncustomerAppCognitoPostConfirmationLambdaExecutionRole": {
        "Fn::GetAtt": [
            "customerAppCognitoPostConfirmation", 
            "Outputs.LambdaExecutionRole"
         ]
    }
}

All 26 comments

@Jkap7788 it is a cyclic dependency as the CLI says. I'm not sure that we've a CLI supported way for this scenario, because the possibility that comes to my mind is adding a custom policy resource which depend on both resources, your user pool and the function, but the CustomResources are getting deployed within the GraphQL API scope.

it seems related to #1874 and #2076

@attilah , do you have any idea how it can be setup manually, by editing the CloudFormation template files for example?

@attilah Thanks for the reply!

I'm currently using a manual workaround for this.

Using amplify update function to add auth permissions to cognito triggers will result in a cyclic dependency error message.
However, the correct IAM policy is added to the function's cloud formation template.
The cognito user pool id is NOT passed down as a parameter so to fix this, I manually modify the IAM policy to include the user pool id in the iam resource arn

@thedgbrt A quick and easy fix is just to manually add the user pool id into the iam policy. Not ideal but gets you unstuck quickly.

@attilah Out of curiosity, how come adding add-to-group functionality to a post confirmation trigger does not register as a cyclic dependency where as adding auth permissions via amplify update function does cause a cyclic dependency?

@attilah I'm also running into a similar issue and would benefit from seeing an example of the setup for a custom policy you're referencing

Same problem - permission appear to be fine bu the environment variable is not set properly. What do I edit to set the environment variable to the correct userpool

I had the same issue. I ended up editing the auth's cloudformation template, rather than the function's.

Look for the comment # Updating lambda role with permissions to Cognito. There will be a policy document section with permissions granted for group creation and adding users to groups. Add the additional policies that you want to the list.

  myAuthPostConfirmationAddToGroupCognito:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: myAuthPostConfirmationAddToGroupCognito
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action:

              - cognito-idp:AdminAddUserToGroup

              - cognito-idp:GetGroup

              - cognito-idp:CreateGroup

             # Add your other permissions here



            Resource: !GetAtt

              - UserPool

              - Arn




      Roles:
        - !Join ['',["myAuthPostConfirmation", '-', !Ref env]]

Ideally the cli would allow us to select which permissions to grant when setting up a trigger instead of assuming these defaults

I have a non-manual way to do this: I create a custom category/resource (i.e. CloudFormation stack) that depends on the function AND the user pool. The custom stack then adds policies to the function after both it and the pool are deployed. If this is interesting to people I'll publish it.

Is there any plan or way the CLI might change to support this function in the future?

@rowanu Awesome! Yes please, would love to see how you implemented it.

Turns out I was relying on a function created directly via the function category (which exports LambdaExecutionRole) in my development environment 🙃 I have submitted a PR to export the role for all the trigger functions, so that I can demo the functionality I mentioned.

Running into this issue as well, but with S3 and Lambda. The workflow looks like this:

  1. Create storage with Amazon S3 and enable and add a Lambda trigger
  2. Would like to give permission to Lambda to write to S3, so we update the Lambda:
$ amplify update function? Please select the Lambda Function you would want to update S3Trigger45ce5c61
? Do you want to update permissions granted to this Lambda function to perform on other resources in your project? Yes
? Select the category storage
> Storage category has a resource called s3cebd22bc
? Select the operations you want to permit for s3cebd22bc create, read, update, delete

Output:

You can access the following resource attributes as environment variables from your Lambda function
var environment = process.env.ENV
var region = process.env.REGION
var storageS3cebd22bcBucketName = process.env.STORAGE_S3CEBD22BC_BUCKETNAME

Error: Cannot add S3Trigger45ce5c61 due to a cyclic dependency
    at checkForCyclicDependencies (/Users/dabit/.nvm/versions/node/v10.16.3/lib/node_modules/@aws-amplify/cli/lib/extensions/amplify-helpers/update-amplify-meta.js:188:15)
    at AmplifyToolkit.updateamplifyMetaAfterResourceUpdate [as _updateamplifyMetaAfterResourceUpdate] (/Users/dabit/.nvm/versions/node/v10.16.3/lib/node_modules/@aws-amplify/cli/lib/extensions/amplify-helpers/update-amplify-meta.js:101:9)
    at Object.updateResource (/Users/dabit/.nvm/versions/node/v10.16.3/lib/node_modules/@aws-amplify/cli/node_modules/amplify-category-function/provider-utils/awscloudformation/index.js:242:21)
    at process._tickCallback (internal/process/next_tick.js:68:7)

The use case here is resizing an image and writing back to S3

cc @kaustavghosh06

@dabit3 The Lambda trigger should already have the permissions to read write to the S3 bucket. So you don’t need to explicitly run ‘amplify update function’

Ah interesting, from my initial test it dit not. Will give it another shot and report back.

Based on my testing, the issue actually has to do with this flow only:

  1. Create storage
  2. Add a new function separately. In the following steps, allow the function to have access to storage create, read, update, delete (this seems like a logical thing to do)
  3. When I update storage to add a trigger, I get a cyclic dependency error

This is probably not a high priority error since most people will probably be instead adding a lambda trigger by configuring storage (amplify update storage) and adding the function from there.

Another way around this is, when adding a new function, __not__ to add permissions for storage and then in the storage walkthrough allow the permissions to be set there.

@rowanu

I have a non-manual way to do this: I create a custom category/resource (i.e. CloudFormation stack) that depends on the function AND the user pool. The custom stack then adds policies to the function after both it and the pool are deployed. If this is interesting to people I'll publish it.

Any chance you can share how you are accomplishing this? It looks like your PR was merged.
This seems to be a very common problem with circular dependencies that many people are having.
I commented on this ticket asking for Amplify docs that sounds like what you are describing:
https://github.com/aws-amplify/amplify-cli/issues/1874#issuecomment-575254839

Thanks for any knowledge sharing!
Erik

I am also running into this issue and it appears my postConfirmation lambda is just using the default value provided for my UserPoolId output because that function never receives the output from the User Pool.

Hi @royalaid @paulsson ,

not sure if this is the correct approach? But basically my solution involves the following:

  • editing my existing post confirmation trigger lambda business logic to make use of cognito (figure 1)
  • editing the post confirmation trigger lambda's cloudformation template, adding the LambdaExecutionRole resource's name as an output (figure 2)
  • adding a custom resource which depends on the cognito user pool id value and the post confirmation trigger lambda's Lambda execution Role name output value and this custom resource will create the IAM policy to access the given cognito user pool (using the user pool id reference) and attach it to the lambda's role based on the LambdaExecutionRole name reference (figure 3)

Doing the above whilst reading this section of the docs on deploying custom resources made sense to me: https://aws-amplify.github.io/docs/cli-toolchain/quickstart#custom-cloudformation-stacks

figure 1:

const cognitoIdentityServiceProvider = new CognitoIdentityServiceProvider()

figure 2:

{
  ....,
  "Outputs": {
    ....,
    "LambdaExecutionRole": {
      "Value": {
        "Ref": "LambdaExecutionRole"        
      }
    }
  }
}

figure 3:
backend-config.json

{
  ...,
"cognitoLambdaTriggerPermissions": {
        "postConfirmationPermissions": {
            "service": "Cognito-Lambda-Trigger-Permissions",
            "providerPlugin": "awscloudformation",
            "dependsOn": [
                {
                    "category": "auth",
                    "resourceName": "customerAppCognito",
                    "attributes": [
                        "UserPoolId"
                    ]
                },
                {
                    "category": "function",
                    "resourceName": "customerAppCognitoPostConfirmation",
                    "attributes": [
                        "LambdaExecutionRole"
                    ]
                }
            ]
        }
    }
}

cognitoLambdaTriggerPeromissions/postConfirmationPermissions/template.json

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "Resource stack to apply cognito permissions to circular dependency lambda",
    "Parameters": {
        "env": {
            "Type": "String"
        },
        "authcustomerAppCognitoUserPoolId": {
            "Type": "String"        
        },
        "functioncustomerAppCognitoPostConfirmationLambdaExecutionRole": {
            "Type": "String"        
        }
    },
    "Conditions": {

    },
    "Resources": {
        "PostConfirmationCognitoResourcesPolicy": {
            "Type": "AWS::IAM::Policy",
            "Properties": {
                "PolicyName": "post-confirmation-cognito-execution-policy",
                "Roles": [
                    {
                        "Ref": "functioncustomerAppCognitoPostConfirmationLambdaExecutionRole"
                    }
                ],
                "PolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Action": [
                                "cognito-identity:Describe*",
                                "cognito-identity:Get*",
                                "cognito-identity:List*",
                                "cognito-idp:Describe*",
                                "cognito-idp:AdminGetDevice",
                                "cognito-idp:AdminGetUser",
                                "cognito-idp:AdminList*",
                                "cognito-idp:List*",
                                "cognito-sync:Describe*",
                                "cognito-sync:Get*",
                                "cognito-sync:List*",
                                "iam:ListOpenIdConnectProviders",
                                "iam:ListRoles",
                                "sns:ListPlatformApplications",
                                "cognito-idp:ForgotPassword",
                                "cognito-idp:UpdateAuthEventFeedback",
                                "cognito-idp:UpdateResourceServer",
                                "cognito-idp:UpdateUserPoolClient",
                                "cognito-idp:AdminUpdateUserAttributes",
                                "cognito-idp:UpdateUserAttributes",
                                "cognito-idp:UpdateUserPoolDomain",
                                "cognito-idp:UpdateIdentityProvider",
                                "cognito-idp:UpdateGroup",
                                "cognito-idp:AdminUpdateAuthEventFeedback",
                                "cognito-idp:UpdateDeviceStatus",
                                "cognito-idp:UpdateUserPool"
                            ],
                            "Resource": [
                                {
                                    "Fn::Join": [
                                        "",
                                        [
                                            "arn:aws:cognito-idp:",
                                            {
                                                "Ref": "AWS::Region"
                                            },
                                            ":",
                                            {
                                                "Ref": "AWS::AccountId"
                                            },
                                            ":userpool/",
                                            {
                                                "Ref": "authcustomerAppCognitoUserPoolId"
                                            }
                                        ]
                                    ]
                                }
                            ]
                        }
                    ]
                }
            }
        }
    },
    "Outputs": {

    }
}

cognitoLambdaTriggerPeromissions/postConfirmationPermissions/parameters.json

{
    "authcustomerAppCognitoUserPoolId": {  
       "Fn::GetAtt": [
          "customerAppCognito", 
          "Outputs.UserPoolId"
       ]
    },
    "functioncustomerAppCognitoPostConfirmationLambdaExecutionRole": {
        "Fn::GetAtt": [
            "customerAppCognitoPostConfirmation", 
            "Outputs.LambdaExecutionRole"
         ]
    }
}

Any update on this issue folks? I'm trying to update my post-confirmation lambda trigger to run some business logic when the users sign up. What's the best approach currently to implement this?
Best regards,
Kyle

@kylekirkby For your use-case, did you try adding and testing the post-confirmation auth trigger? What's the error that you're getting? The Trigger should already be populated with Cognito user-pool information which is what you're looking for out here?

Hi @kaustavghosh06 ,

Thanks for your reply! I've already got the post Confirmation Trigger added. This successfully adds the users to the "free" cognito user group - works a treat! However, I need to generate an API key for the users upon signup. For the life of me I cannot give the postConfirmation trigger lambda function permission to access my GraphQL API - I'm getting a circular dependency issue (I'm also version 4.21.0 for the CLI if that helps). Any ideas how I can achieve this? I've currently got users clicking a button to trigger a lambda function that generates the keys but would like this to be done automatically on sign up.

I did also comment on this issue: https://github.com/aws-amplify/amplify-cli/issues/1874#issuecomment-575254839

Best regards,

Kyle

@kylekirkby Yes, we're aware of this circular dependency issue and it's explained more in detail out here - https://github.com/aws-amplify/amplify-cli/issues/1874#issuecomment-606073890
Did you try out the workaround mentioned here - https://github.com/aws-amplify/amplify-cli/issues/1874#issuecomment-619316678
While we work in resolving this - can't promise an ETA at this point - you can give the workaround a try since it seems to have worked for other customers as well. Thank you!

Hi @kaustavghosh06 ,

I did try the work arounds that other users have tried but to no avail. This may work in allowing the post confirmation function to have permission to access my graphql api but I don’t get the variables added to the process. How do I go about accessing the graphql api output from the post confirmation function?

Running into the same on S3 triggers to resize.

Approaches tried:
1)Added trigger as part of S3 creation.
2)Also tried adding a function, and then adding as a trigger.
Both the cases "Access Denied" when writing back to s3.

Tried adding permissions and ran into cyclic dependency.

cc:
@kaustavghosh06
@dabit3

I have the same problem and tried the same as @kkrao2301 — no solution.

I was having this same issue and @danielblignaut's solution here worked wonders. As a matter of fact, it was simpler than that because I only needed to edit backend-config.json and create the CloudFormation template for that resource. The Outputs key in his figure 2 was already added to my lambda template and the parameters.json wasn't needed because the CLI created them for me automatically in nested-cloudformation-stack.yml.

I did have to amplify env checkout {your-env} for the CLI to pick up the changes to backend-config.json.

I was having this issue. And just added the permission like noted here and then did amplify push and all is well.

Few had proposed the solution and @danielblignaut straigh forward answer helped me to solve the issue. xoxo

Was this page helpful?
0 / 5 - 0 ratings