Amplify-cli: Create Custom Attributes in cognito from amplify cli

Created on 3 Apr 2019  路  29Comments  路  Source: aws-amplify/amplify-cli

Note: If your question is regarding the AWS Amplify Console service, please log it in the
official AWS Amplify Console forum

* Which Category is your question related to? *
Amplify Auth

* What AWS Services are you utilizing? *
Cognito

* Provide additional details e.g. code snippets *
Is there a way to create custom attributes in cognito during
amplify auth add
or by editing parameters.json

auth feature-request

Most helpful comment

@thedgbrt If you want to automate adding custom attributes today you can do this by creating a Custom Resource and use the aws-sdk to make the right API calls. A side benefit of using the API instead of cloudformation is that updates do not cause your user pool to be replaced.

You would need to use the amplify cli to add a new function, and allow it access to the auth category. Then you will have an environment variable "AUTH_COGNITO${project_name}_USERPOOLID" available in your lambda.

To have your Lambda run when you run amplify push, modify the function's Cloudformation template to add the custom resource inside the "Resources" object:

"CognitoAddCustomAttribute": {
    "Type": "Custom::AddCustomAttribute",
    "Properties": {
        "ServiceToken": {
            "Fn::GetAtt": [
                "LambdaFunction",
            "Arn"
        ]
    },
    "TriggerVersion": 1
    }
},

your Lambda would have in its main body (for a numeric attribute):

    const aws = require("aws-sdk");
    const identity = new aws.CognitoIdentityServiceProvider({
        apiVersion: "2016-04-18",
        region: process.env.REGION
      });
    await identity
        .addCustomAttributes({
          UserPoolId: process.env.USERPOOL,
          CustomAttributes: [
            {
              Name: "myCustomAttribute",
              AttributeDataType: "Number",
              Mutable: true,
              Required:false ,
              NumberAttributeConstraints: {
                MinValue: "0",
                MaxValue: "1000"
              }
            }
          ]
        })
        .promise()

Note that "myCustomAttribute" will be accessible as "custom:myCustomAttribute" for users, Cognito appends the "custom:" part for you.

The template property "TriggerVersion" can be incremented any time you need your function to be re-run in an environment.

I would also modify the "AmplifyResourcesPolicy" to only allow the specific actions you need to add a custom resource.

All 29 comments

@achutkiran Are you referring to the social provider custom attributes?

User pool custom attributes like customer type

+1

You can manually change the cognito-cloudformation-template.yml file after adding the authentication (but before running amplify push.

AWS properties Cognito UserPool

_Snippet cognito-cloudformation-template.yml_

....
# BEGIN USER POOL RESOURCES
  UserPool:
  # Created upon user selection
  # Depends on SNS Role for Arn if MFA is enabled
    Type: AWS::Cognito::UserPool
    UpdateReplacePolicy: Retain
    Properties:
      UserPoolName: !If [ShouldNotCreateEnvResources, !Ref userPoolName, !Join ['',[!Ref userPoolName, '-', !Ref env]]]
      Schema:
        - Name: myCustomAttribute
          Required: true
          Mutable: true
          AttributeDataType: String

        - Name: myOtherCustomAttribute
          Required: false
          Mutable: true
          AttributeDataType: String

      AutoVerifiedAttributes: !Ref autoVerifiedAttributes
      Policies:
        PasswordPolicy:
          MinimumLength: !Ref passwordPolicyMinLength
          RequireLowercase: true
          RequireNumbers: true
          RequireSymbols: true
          RequireUppercase: true
      MfaConfiguration: !Ref mfaConfiguration
      SmsVerificationMessage: !Ref smsVerificationMessage
      SmsConfiguration:
        SnsCallerArn: !GetAtt SNSRole.Arn
        ExternalId: !Ref roleExternalId
...

When you add new environments this template will be used for creating the new user pools too.

Presently I am editing cloudFormationTemplate and parameters.json to add custom Attributes and groups. But problem is for any reason whenever i update auth i have to update template manually. So it will be better if custom Attributes are included in amplify cli.

@achutkiran You're right, we don't have in-built support for custom attributes yet and what you're trying out is currently the best way to go about it, but I'll tag this as a feture request and prioritize work on this.

@timkuilman @achutkiran Yes I figured that out. I prefer editing the CF templates as well. Been a while since I've been in there. But not too hard to follow. And it would be impossible to have all the AWS service options in the cli anyways. Thanks.

Thanks

I tried https://github.com/aws-amplify/amplify-cli/issues/1204#issuecomment-482028480 and update auth, but an update error occured.

cloudformation.yml

        - Name: custom:store_name
          Required: true
          Mutable: true
          AttributeDataType: String

parameter.json

    "requiredAttributes": [
        "...",
        "custom:store_name"
    ],

Am I wrong in specifying?

@yukitaka13-1110 required attributes in Cognito must be defined in Cloudformation templates when you first create the user pool. Cognito does not allow this update. Here is a forum thread on the topic: https://forums.aws.amazon.com/thread.jspa?messageID=898280&#898280

@RossWilliams I tried https://github.com/aws-amplify/amplify-cli/issues/1204#issuecomment-482028480.
In this case, dose the myCustomAttribute stands for custom attribute(ex user_type) ?
about https://github.com/aws-amplify/amplify-cli/issues/1761#issuecomment-507007122
I want to add custom attributes like user_type instead of required attribute.
Is it possible to add custom attributes by adding custom attributes in the cloudformation template and 'amplify push'?

@yukitaka13-1110 An example https://github.com/aws-amplify/amplify-cli/pull/1289/commits/83476b944852ffa000875074e200b4fd5ec6f88c?short_path=ac1bfc4#diff-ac1bfc471563fa82f8882dccfe0ec1c2.

My template looks like this:

Schema:
  -
    AttributeDataType: "String"
    Mutable: true
    Name: roles
    StringAttributeConstraints:
      MaxLength: 256
      MinLength: 1

And it adds the custom attribute just fine.

@yukitaka13-1110 the important part of https://github.com/aws-amplify/amplify-cli/issues/1204#issuecomment-482028480 is that you must modify the cloudformation template before running amplify push. If you have an existing user pool, then you have already run push and it is too late to change Cognito without creating a new user pool.

myCustomAttribute is the name of the attribute.

Required fields cannot be added after you create a user pool. Optional fields are ok to add inside the Cognito console, but not in Cloudformation template. If you use the Cognito console you can set these optional attributes without creating a new user pool. Additionally, you can write a Custom Resource and use the AWS SDK to add non-required custom properties if you don't want to use the console.

The Cloudformation documentation here says that modifying the Schema field requires a replacement of the user pool, meaning it will not update an existing pool.

@RossWilliams Oh, I see. I'll try it
Thank you for answering in detail !

@RossWilliams I could do it.
Thank you so much !

Just FYI for anyone else attempting this, but the example shown in https://github.com/aws-amplify/amplify-cli/issues/1204#issuecomment-482028480 doesn't work exactly because custom attributes cannot be Required

Reason: Required custom attributes are not supported currently. (Service: AWSCognitoIdentityProviderService; Status Code: 400; Error Code: InvalidParameterException;

Therefore, you must omit the Required property.

I would happy to be proven wrong if anyone has a way to get Required custom attributes working?

@kaustavghosh06 any progress on this? Would be much cleaner to simply add custom attributes through the amplify cli. Right now if I want to add a custom attribute in multiple environments, I need to do so manually on each cognito user pool. This can lead to errors.

@thedgbrt If you want to automate adding custom attributes today you can do this by creating a Custom Resource and use the aws-sdk to make the right API calls. A side benefit of using the API instead of cloudformation is that updates do not cause your user pool to be replaced.

You would need to use the amplify cli to add a new function, and allow it access to the auth category. Then you will have an environment variable "AUTH_COGNITO${project_name}_USERPOOLID" available in your lambda.

To have your Lambda run when you run amplify push, modify the function's Cloudformation template to add the custom resource inside the "Resources" object:

"CognitoAddCustomAttribute": {
    "Type": "Custom::AddCustomAttribute",
    "Properties": {
        "ServiceToken": {
            "Fn::GetAtt": [
                "LambdaFunction",
            "Arn"
        ]
    },
    "TriggerVersion": 1
    }
},

your Lambda would have in its main body (for a numeric attribute):

    const aws = require("aws-sdk");
    const identity = new aws.CognitoIdentityServiceProvider({
        apiVersion: "2016-04-18",
        region: process.env.REGION
      });
    await identity
        .addCustomAttributes({
          UserPoolId: process.env.USERPOOL,
          CustomAttributes: [
            {
              Name: "myCustomAttribute",
              AttributeDataType: "Number",
              Mutable: true,
              Required:false ,
              NumberAttributeConstraints: {
                MinValue: "0",
                MaxValue: "1000"
              }
            }
          ]
        })
        .promise()

Note that "myCustomAttribute" will be accessible as "custom:myCustomAttribute" for users, Cognito appends the "custom:" part for you.

The template property "TriggerVersion" can be incremented any time you need your function to be re-run in an environment.

I would also modify the "AmplifyResourcesPolicy" to only allow the specific actions you need to add a custom resource.

@RossWilliams thank you for taking the time to explain, made me realize amplify was much more powerful than I thought 馃憤

@RossWilliams I鈥檓 trying to take the custom resource approach you outlined, but my CF stack seems to hang during amplify push. The function is run successfully and I see the custom attribute added to my user pool, but the amplify operation never completed (at least not before I cancel ~30 mins in). I think the custom resource may need to leverage cfn-response? I tried with no success. Any tips?

@adamelmore, yes, look at 'cfn-response' or 'safe-cfn-custom-resource' to make sure you return a proper response for cloudformation, otherwise your stack with be stuck in an updating state until it times out. Sorry I didn't mention that cloudformation needs a specific response object from your lamba.

I had the same problem as @adamelmore and I solve this by using cfn-custom-resource package.

exports.handler = async function (event, context) { //eslint-disable-line
const cfnCR = require('cfn-custom-resource');
const { configure, sendSuccess, sendFailure, /*sendResponse,*/ LOG_VERBOSE, /*SUCCESS*/ } = cfnCR;
...

// For Delete requests, immediately send a SUCCESS response.
if (event.RequestType === "Delete") {
      /* Resource successfully created! - async/await */
      const result = await sendSuccess(physicalResourceId, {}, event);
      return result;
}

....

/* Resource successfully created! - async/await */
const result = await sendSuccess(physicalResourceId, {}, event);

or 

await sendFailure(err, event); // if it fails

@SanvirDessai I think this is about the sign-up request use a custom attribute set up in advance on the Cognito pool and doesn't automatically create such custom attribute. When I tested, I got a "Attributes did not conform to the schema: custom:X: Attribute does not exist in the schema."

I'm wondering if I miss something. Did you tested/got this working?

@rfpedrosa you are correct, you cannot create a custom attribute that is not part of the Cognito Attributes config. This is a low priority issue for us b/c it's mostly plug-and-play when adding new attributes.

It would be cool if Amplify could detect the custom attributes provisioned for a Cognito user pool -- which would be trivial if the CLI supported adding custom attributes! -- and plug those into an Amplify-generated migration trigger lambda, so existing attributes are copied over without needing to override the generated migration lambda.

I think it's kind of ridiculous that you can't create a custom Attribute via the CLI after the cognito pool is created and pushed. So basically if you miss something or down the road want to add something in anyway whatsoever you have to completely remake the pool? Especially since this has been alive for a year.

@JNeim

So basically if you miss something or down the road want to add something in anyway whatsoever you have to completely remake the pool? Especially since this has been alive for a year.

No, you can still add custom attributes using the AWS-cli per above comments. You do not need to replace the pool.

Isn't creating a Profile model in the GraphQL a better approach to add custom fields?

There is another problem here, when using custom attribute, you can only set MaxLength, but on the cognito console, the unit is byte, it is maxByte in the console, and it is 2048, but MaxLength in cloudformation only 256 or something.

Here's a shell script to create custom attributes using AWS CLI:

#!/bin/sh
aws cognito-idp add-custom-attributes \
--user-pool-id <value> \
--custom-attributes \
'[
  {
    "Name": "string",
    "AttributeDataType": "String"|"Number"|"DateTime"|"Boolean",
    "DeveloperOnlyAttribute": true|false,
    "Mutable": true|false,
    "Required": true|false,
    "NumberAttributeConstraints": {
      "MinValue": "string",
      "MaxValue": "string"
    },
    "StringAttributeConstraints": {
      "MinLength": "string",
      "MaxLength": "string"
    }
  }
  ...
]`

I couldn't get the custom resource method right, but this worked for me.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

amlcodes picture amlcodes  路  3Comments

mwarger picture mwarger  路  3Comments

gabriel-wilkes picture gabriel-wilkes  路  3Comments

adriatikgashi picture adriatikgashi  路  3Comments

ffxsam picture ffxsam  路  3Comments