kakakakakku blog

Weekly Tech Blog: Keep on Learning!

crossRegionReferences: AWS CDK で Cognito User Pools にカスタムドメインを設定する

Amazon Cognito User Pools にカスタムドメインを設定する場合,内部的に追加される Amazon CloudFront で「バージニア北部リージョン (us-east-1)」の AWS Certificate Manager (ACM) 証明書が必要になる📝

しかし AWS CDK(もしくは AWS CloudFormation)ではクロスリージョンスタックリファレンス(リージョン間のクロススタックリファレンス)ができず,実装するときに AWS Systems Manager Parameter Store を使ったり,Props に静的に ARN (Amazon Resource Names) を設定したり,何かしら考慮が必要になるという課題がある.

docs.aws.amazon.com

ちなみに Amazon Cognito User Pools のカスタムドメインに限った話ではなく,例えば Amazon CloudFront と Amazon S3 を組み合わせる場合にも出てくる仕様になる.

docs.aws.amazon.com

実際に試すと以下のエラーが出る🔥

Cross stack references are only supported for stacks deployed to the same environment or between nested stacks and their parent stack.

crossRegionReferences プロパティ

現在まだ実験的機能 (experimental) ではあるけど,AWS CDK の crossRegionReferences プロパティを使うとイイ感じにクロスリージョンスタックリファレンス(リージョン間のクロススタックリファレンス)を実現できる👌

仕組みをザックリと説明すると,AWS CloudFormation のカスタムリソース (AWS Lambda 関数) で us-east-1 リージョンの AWS Certificate Manager 証明書 ARN を ap-northeast-1 リージョンの AWS Systems Manager Parameter Store に登録して,ap-northeast-1 リージョンの AWS CloudFormation スタックでは AWS Systems Manager Parameter Store の動的参照を使って AWS Certificate Manager 証明書 ARN を取得する.ザックリではあるけどアーキテクチャ図も描いておいた❗️

詳細な仕組みは AWS CDK ドキュメントを参照してもらえればと📝

docs.aws.amazon.com

サンプルコード

動作確認に使ったサンプルコードを載せておく👌

👾 bin/cdk.ts

ここで crossRegionReferences プロパティを設定している❗️そして AWS Certificate Manager 証明書 ARN を CognitoCustomDomainStack に渡している.

const envVirginia = {
    account: '000000000000',
    region: 'us-east-1',
}

const envTokyo = {
    account: '000000000000',
    region: 'ap-northeast-1',
}

const cognitoCustomDomainAcmStack = new CognitoCustomDomainAcmStack(app, 'CognitoCustomDomainAcmStack', {
    env: envVirginia,
    crossRegionReferences: true,
})

new CognitoCustomDomainStack(app, 'CognitoCustomDomainStack', {
    env: envTokyo,
    crossRegionReferences: true,
    certificate: cognitoCustomDomainAcmStack.certificate,
})

👾 cognito-custom-domain-acm.ts

AWS Certificate Manager 証明書を作ってクロススタックで取得できるようにしておく.今回は userpool.xxxxx.net というカスタムドメインにした📝

import {
    Stack,
    StackProps,
    aws_certificatemanager,
    aws_route53,
} from 'aws-cdk-lib'
import { Construct } from 'constructs'

export class CognitoCustomDomainAcmStack extends Stack {
    public readonly certificate: aws_certificatemanager.Certificate

    constructor(scope: Construct, id: string, props: StackProps) {
        super(scope, id, props)

        const hostedZone = aws_route53.HostedZone.fromHostedZoneAttributes(this, 'HostedZone', {
            hostedZoneId: 'XXXXX',
            zoneName: 'xxxxx.net',
        })

        this.certificate = new aws_certificatemanager.Certificate(this, 'Certificate', {
            domainName: 'userpool.xxxxx.net',
            validation: aws_certificatemanager.CertificateValidation.fromDns(hostedZone),
        })
    }
}

👾 cognito-custom-domain.ts

AWS Certificate Manager 証明書を ARN を StackProps から取得して,Amazon Cognito User Pools のカスタムドメインに設定する👌

import {
    Stack,
    StackProps,
    aws_certificatemanager,
    aws_cognito,
    aws_route53,
    aws_route53_targets,
} from 'aws-cdk-lib'
import { Construct } from 'constructs'

interface CustomStackProps extends StackProps {
    certificate: aws_certificatemanager.Certificate
}

export class CognitoCustomDomainStack extends Stack {
    constructor(scope: Construct, id: string, props: CustomStackProps) {
        super(scope, id, props)

        const userPool = new aws_cognito.UserPool(this, 'UserPool', {
            userPoolName: 'sandbox',
        })

        const userPoolDomain = userPool.addDomain('UserPoolDomain', {
            customDomain: {
                domainName: 'userpool.xxxxx.net',
                certificate: props.certificate,
            },
        })

        const hostedZone = aws_route53.HostedZone.fromHostedZoneAttributes(this, 'HostedZone', {
            hostedZoneId: 'XXXXX',
            zoneName: 'xxxxx.net',
        })

        new aws_route53.ARecord(this, 'ApexARecod', {
            zone: hostedZone,
            target: aws_route53.RecordTarget.fromIpAddresses('8.8.8.8'),
        })

        new aws_route53.ARecord(this, 'UserPoolARecord', {
            zone: hostedZone,
            recordName: 'userpool',
            target: aws_route53.RecordTarget.fromAlias(new aws_route53_targets.UserPoolDomainTarget(userPoolDomain)),
        })
    }
}

デプロイ確認

実際にデプロイした結果(既にリソースは削除済)を抜粋して載せておく👌

👇️ us-east-1 リージョンに AWS Certificate Manager 証明書を登録できた

👇️ ap-northeast-1 リージョンの AWS Systems Manager Parameter Store に AWS Certificate Manager 証明書 ARN が登録されていた

👇️ ap-northeast-1 リージョンの Amazon Cognito User Pools に us-east-1 リージョンの AWS Certificate Manager 証明書が登録されていた

まとめ

AWS CDK の crossRegionReferences プロパティを使って,Amazon Cognito User Pools にカスタムドメインを設定してみた.イイ感じにクロスリージョンスタックリファレンス(リージョン間のクロススタックリファレンス)を実現できて便利だった❗️

しかし現状ではまだ「実験的機能 (experimental)」でプロダクション環境では採用しにくく,最近似たような構成を仕事で実装したときは crossRegionReferences プロパティは使わずに独自に実装をした.正式リリースを待つぞー \( 'ω')/

参考リンク

AWS CloudFormation のカスタムリソース実装は以下にある🔗

github.com

github.com