简体   繁体   中英

Creating Cognito User Pool With Custom Domain name from AWS CDK

I'm trying to creating Cognito user pool with a custom domain name through AWS CDK. I manage to get everyting working untill to the point where I needed to create an A record in the Rout53 hosted zone. I searched through all the documents but coudn't find a way to do that. Following is my code. Any help would be much appriciated.

      const cfnUserPool = new CfnUserPool(this, 'MyCognitoUserPool', {
            userPoolName: 'MyCognitoUserPool',
            adminCreateUserConfig: {
                allowAdminCreateUserOnly: false
            },
            policies: {
                passwordPolicy: {
                    minimumLength: 8,
                    requireLowercase: true,
                    requireNumbers: true,
                    requireSymbols: true,
                    requireUppercase: true,
                    temporaryPasswordValidityDays: 30
                }
            },
            usernameAttributes: [
                UserPoolAttribute.EMAIL
            ],
            schema: [
                {
                    attributeDataType: 'String',
                    name: UserPoolAttribute.EMAIL,
                    mutable: true,
                    required: true
                },
                {
                    attributeDataType: 'String',
                    name: UserPoolAttribute.FAMILY_NAME,
                    mutable: false,
                    required: true
                },
                {
                    attributeDataType: 'String',
                    name: UserPoolAttribute.GIVEN_NAME,
                    mutable: false,
                    required: true
                }
            ]
        });

      const cognitoAppDomain = new CfnUserPoolDomain(this, "PigletAuthDomainName", {
            domain: authDomainName,
            userPoolId: cfnUserPool.ref,
            customDomainConfig: {
                certificateArn: 'ACM Certificate arn'
            }
        });

    /* 
      TODO: Create an A record from the created cnfUserPoolDomain
    */

Everything works up untill to this point. Now the question is how to create an A record using the CfnUserPoolDomain

Any help is much appriciated.

Update May 2020

The UserPoolDomain construct has been extended and a UserPoolDomainTarget was added to provide this functionality.

Now, all you need to do is the following:

const userPoolDomain = new cognito.UserPoolDomain(this, 'UserPoolDomain', {
  userPool,
  customDomain: {
    domainName: authDomainName,
    certificate,
  },
});

new route53.ARecord(this, 'UserPoolCloudFrontAliasRecord', {
  zone: hostedZone,
  recordName: authDomainName,
  target: route53.RecordTarget.fromAlias(new route53_targets.UserPoolDomainTarget(userPoolDomain)),
});

I had the same Problem, It looks like CloudFormation does not have a return parameter for the CfnUserPoolDomain AliasTarget. Which means the cdk can not provide this parameter either.

I ended up implementing it using the AWS SDK ( npm install aws-sdk ) and getting the value using the APIs:


Update: The better solution is to use the AwsCustomResource . You can see a detailed example in aws/aws-cdk (#6787) :

const userPoolDomainDescription = new customResources.AwsCustomResource(this, 'user-pool-domain-description', {
  onCreate: {
    physicalResourceId: 'user-pool-domain-description',
    service: 'CognitoIdentityServiceProvider',
    action: 'describeUserPoolDomain',
    parameters: {
      Domain: userPoolDomain.domain
    }
  }
});

const dnsName = userPoolDomainDescription.getData('DomainDescription.CloudFrontDistribution').toString();

// Route53 alias record for the UserPoolDomain CloudFront distribution
new route53.ARecord(this, 'UserPoolDomainAliasRecord', {
  recordName: userPoolDomain.domain,
  target: route53.RecordTarget.fromAlias({
    bind: _record => ({
      hostedZoneId: 'Z2FDTNDATAQYW2', // CloudFront Zone ID
      dnsName: dnsName,
    }),
  }),
  zone,
})

Here's how to get around it. Assuming you have a stack.yaml that you deploy with a CI tool, say through bash:

THE_STACK_NAME="my-cognito-stack"
THE_DOMAIN_NAME="auth.yourveryowndomain.org"

# get the alias target
# notice that it will be empty upon first launch (chicken and the egg problem)
ALIAS_TARGET=$(aws cognito-idp describe-user-pool-domain --domain ${THE_DOMAIN_NAME} | grep CloudFrontDistribution | cut -d \" -f4)

# create/update the deployment CloudFormation stack
# notice the AliasTarget parameter (which can be empty, it's okay!)
aws cloudformation deploy --stack-name ${THE_STACK_NAME} --template-file stack.yaml --parameter-overrides AliasTarget=${ALIAS_TARGET} DomainName=${THE_DOMAIN_NAME}

The stack.yaml minimal version (remember to fill the UserPool config):

---
AWSTemplateFormatVersion: 2010-09-09

Parameters:
  DomainName:
    Type: String
    Default: auth.yourveryowndomain.org
    Description: The domain name to use to serve this project.

  ZoneName:
    Type: String
    Default: yourveryowndomain.org
    Description: The hosted zone name coming along with the DomainName used.

  AliasTarget: # no default value, can be empty
    Type: String
    Description: The UserPoolDomain alias target.

Conditions: # here's "the trick"
  HasAliasTarget: !Not [!Equals ['', !Ref AliasTarget]]

Resources:
  Certificate:
    Type: "AWS::CertificateManager::Certificate"
    Properties: 
      DomainName: !Ref ZoneName
      DomainValidationOptions:
        - DomainName: !Ref ZoneName
          ValidationDomain: !Ref ZoneName
      SubjectAlternativeNames:
        - !Ref DomainName

  UserPool:
    Type: AWS::Cognito::UserPool
    Properties:
      [... fill that with your configuration! ...]

  UserPoolDomain:
    Type: AWS::Cognito::UserPoolDomain
    Properties:
      UserPoolId: !Ref UserPool
      Domain: !Ref DomainName
      CustomDomainConfig:
        CertificateArn: !Ref Certificate

  DnsRecord: # if AliasTarget parameter is empty, well we just can't do that one!
    Condition: HasAliasTarget # and here's how we don't do it when we can't
    Type: AWS::Route53::RecordSet
    Properties:
      HostedZoneName: !Sub "${ZoneName}."
      AliasTarget:
        DNSName: !Ref AliasTarget
        EvaluateTargetHealth: false
        # HostedZoneId value for CloudFront is always this one
        # see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53-aliastarget.html
        HostedZoneId: Z2FDTNDATAQYW2
      Name: !Ref DomainName
      Type: A

Be aware CloudFormation conditions are not "a trick" at all: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/conditions-section-structure.html . We simply use it as a trick along with the "first launch won't do it all" to get around our scenario.

Kinda weird, but only for the first run! Launch it again: everything is fine.


PS: can't wait to avoid all that by simply having the CloudFrontDistribution alias target directly in the AWS::Cognito::UserPoolDomain return values!!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM