cloudformation-coverage-roadmap
cloudformation-coverage-roadmap copied to clipboard
AWS::Cognito::UserPoolDomain missing return value for the CloudFront target
I am missing the return value for the CloudFront target so that you could create a DNS record in the same template so that you could do this:
DNS:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneName: mydomain.com.
Name: myuserpool.mydomain.com.
Type: A
AliasTarget:
HostedZoneId: Z2FDTNDATAQYW2
DNSName: !GetAtt UserPoolDomain.DomainName
UserPoolDomain:
Type: AWS::Cognito::UserPoolDomain
Properties:
UserPoolId: !Ref UserPool
Domain: myuserpool.mydomain.com
CustomDomainConfig:
CertificateArn: !Ref CertificateArn
Originally posted by @Nr18 in https://github.com/aws-cloudformation/aws-cloudformation-coverage-roadmap/issues/58#issuecomment-539652016
Yeah need this one too.
i am glad i am not the only need this feature... back to write my Cfn custom resources for now
i am glad i am not the only need this feature... back to write my Cfn custom resources for now
Please share it here....until AWS provides proper resolution :)
sorry, as contractor my works are IP of my client. can't share it out
Edit 2: See this comment for the modern AWS CDK version of this.
For those that end up here like I did, as a workaround it's possible to get the CloudFrontDistribution
using the AWS SDK/CLI:
- https://docs.aws.amazon.com/cli/latest/reference/cognito-idp/describe-user-pool-domain.html
- https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityServiceProvider.html#describeUserPoolDomain-property
Edit: My personal workaround using https://github.com/aws/aws-cdk and @aws-cdk/custom-resources.AwsSdkCall
:
- https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_custom-resources.AwsSdkCall.html
const cdk = require('@aws-cdk/core')
const cognito = require('@aws-cdk/aws-cognito')
const cr = require('@aws-cdk/custom-resources')
const route53 = require('@aws-cdk/aws-route53')
// The userPool was defined earlier in the code (not shown here)
// This creates the user pool domain resource
const userPoolDomain = new cognito.CfnUserPoolDomain(
this,
'UserPoolDomain',
{
userPoolId: userPool.userPoolId,
domain: authDomain,
customDomainConfig: {
certificateArn,
},
}
)
userPoolDomain.node.addDependency(userPool)
// This allows us to get the cloudfront distribution using a custom resource that calls the AWS SDK
const describeCognitoUserPoolDomain = new cr.AwsCustomResource(
this,
'DescribeCognitoUserPoolDomain',
{
resourceType: 'Custom::DescribeCognitoUserPoolDomain',
onCreate: {
region: 'us-east-1',
service: 'CognitoIdentityServiceProvider',
action: 'describeUserPoolDomain',
parameters: {
Domain: userPoolDomain.domain,
},
physicalResourceId: cr.PhysicalResourceId.of(userPoolDomain.domain),
},
// TODO: can we restrict this policy more? Get the ARN for the user pool domain? Or the user pool maybe?
policy: cr.AwsCustomResourcePolicy.fromSdkCalls({
resources: cr.AwsCustomResourcePolicy.ANY_RESOURCE,
}),
}
)
describeCognitoUserPoolDomain.node.addDependency(userPoolDomain)
const userPoolDomainDistribution = describeCognitoUserPoolDomain.getResponseField(
'DomainDescription.CloudFrontDistribution'
)
I recently threw together a Custom Resource to do this and released it in AWS SAR: https://serverlessrepo.aws.amazon.com/applications/arn:aws:serverlessrepo:us-east-1:273450712882:applications~amazon-cognito-domain-distribution
Happy to iterate on any improvement ideas
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 🤗
CDK recently added constructs for this. In CDK, all you need to do is:
const userPoolDomain = new cognito.UserPoolDomain(this, 'UserPoolDomain', {
userPool,
customDomain: {
domainName: `auth.${domainName}`,
certificate,
},
});
new route53.ARecord(this, 'UserPoolDomainAliasRecord', {
zone: hostedZone,
recordName: `auth.${domainName}`,
target: route53.RecordTarget.fromAlias(new route53_targets.UserPoolDomainTarget(userPoolDomain)),
});
CDK API reference for UserPoolDomainTarget. In fact, it was @0xdevalias's proposal that was adopted by the CDK team (see here).
Nice to see it solved in the CDK. But we need that for simple Cloudformation templates as well.
duplicate of #241 ?
I will probably utilize this custom resource, and forget (next year or after) when the attribute is built in.
@swoldemi Just tried your custom resource SAR, and it worked a treat. Thanks for sharing!
This may have been fixed recently? I initially hit this and came across this article, then I noticed CloudFrontDistribution
is listed as an attribute in the aws-resource-cognito-userpooldomain documentation
I managed to successfully deploy a AWS::Route53::RecordSet
along with AWS::Cognito::UserPoolDomain
with no workaround. Hopefully this helps someone else who ends up on this path.
Using the original example posted the CloudFormation Template looks like this:
AuthDomain:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneId: !Ref HostedZoneId
Domain: !Ref AuthDomainName
Type: 'A'
AliasTarget:
DNSName: !GetAtt UserPoolDomain.CloudFrontDistribution
HostedZoneId: Z2FDTNDATAQYW2 # For CloudFront, HostedZoneId is always Z2FDTNDATAQYW2
EvaluateTargetHealth: false
UserPoolDomain:
Type: AWS::Cognito::UserPoolDomain
Properties:
UserPoolId: !Ref UserPool
Domain: !Ref AuthDomainName
CustomDomainConfig:
CertificateArn: !Ref DomainCertificateArn
I needed to give my user cognito-idp:DescribeUserPoolDomain
permission for it to read this value, just in case you have tight IAM policies.
Yup, looks like it was added here in February