小西秀和です。
「AWS LambdaカスタムリソースでAWS Cloudformationスタックを別リージョンにデプロイする」の記事でAWS Cloudformationスタックを別リージョンにデプロイするAWS Lambdaカスタムリソースの実装例を説明しました。
また、そのカスタムリソースの使用例として次の記事を書きました。
- AWS LambdaカスタムリソースでACM証明書を作成するAWS Cloudformationスタックを別のリージョンにデプロイする
- AWS Lambdaカスタムリソースで基本認証用Lambda@Edgeを作成するAWS Cloudformationスタックを別リージョンにデプロイする
- AWS LambdaカスタムリソースでCloudFrontオリジンフェイルオーバー用S3バケットを作成するAWS Cloudformationスタックを別リージョンにデプロイする
今回は総まとめという位置づけでカスタムリソースを使用して、今までの記事で紹介したSSL証明書・基本認証・フェイルオーバー構成をまとめてAmazon S3+Amazon CloudFrontに追加する方法を紹介します。
AWS Cloudformationスタックを別リージョンにデプロイするAWS Lambdaカスタムリソースについては元記事を参照してください。
※本記事および当執筆者のその他の記事で掲載されているソースコードは自主研究活動の一貫として作成したものであり、動作を保証するものではありません。使用する場合は自己責任でお願い致します。また、予告なく修正することもありますのでご了承ください。
本記事で試す内容の概要図
今回は次の内容をAWS Cloudformationとカスタムリソースを中心に試しています。
【初回作成時】
- 呼出元Cloudformationスタックは呼出元とカスタムリソースの双方のリージョンで使用するOriginAccessIdentity(OAI)を作成する
- 呼出元Cloudformationスタックは次の内容をus-east-1にデプロイするカスタムリソースをそれぞれ呼び出し、作成した結果を呼出元Cloudformationスタックに返却する
- AWS Certificate Manager(ACM)証明書
- フェイルオーバー用セカンダリAmazon S3バケット
- 呼出元Cloudformationスタックは呼出元リージョンのリソースを作成し、カスタムリソースからの返却値を使用してus-east-1リージョンのリソースを関連付ける
【2回目更新時】
- 呼出元Cloudformationスタックは次の内容をus-east-1にデプロイするカスタムリソースを呼び出し、作成した結果を呼出元Cloudformationスタックに返却する
- 基本認証をするAWS Lambda@Edgeとそのバージョン
- 呼出元Cloudformationスタックは呼出元リージョンのリソースを更新し、カスタムリソースからの返却値を使用してus-east-1リージョンのリソースを関連付ける

各ファイル(テンプレート、関数)の名称設定
AWS CloudformationテンプレートやAWS Lambdaカスタムリソースそれぞれが一意に必要なものを識別して連携できれば各ファイルの名称は任意ですが、説明のために次のように各ファイルの名称を設定します。
- 呼出元AWS Cloudformationテンプレート
⇒ OrgSourceOfRunCfnACMS3LambdaEdgeOtherRegion.yml - us-east-1にセカンダリオリジン用Amazon S3バケットを作成するAWS CloudformationスタックをデプロイするAWS Lambdaカスタムリソース
⇒ CustomResourceToDeployCloudformationStack - ACM証明書を作成するAWS Cloudformationテンプレート
⇒ CfnACMCertificate.yml - セカンダリオリジン用Amazon S3バケットを作成するAWS Cloudformationテンプレート
⇒ CfnS3OtherRegion.yml - Amazon CloudFront用のLambda@Edgeを作成するAWS Cloudformationテンプレート
⇒ CfnBasicAuthLambdaEdge.yml
各ファイルやパラメータの例
各ファイルやパラメータの例を記載していきます。
特に入力パラメータに関しては入力形式を知っていただくための例なので、使用する場合は各パラメータを要件に合わせて設定する必要があります。
呼出元AWS Cloudformationテンプレートへの入力パラメータ例
後述するカスタムリソースの呼出元となるAWS Cloudformationテンプレートに入力するパラメータの例です。
入力値は例なので使用する場合は各パラメータを要件に合わせて設定する必要があります。
次にYAML形式にコメントする形で説明しています。
これらのパラメータはAWSマネジメントコンソールから呼出元AWS Cloudformationテンプレートを実行する場合は、各パラメータを手動入力する必要があります。
#【共通】作成するS3バケットのサフィックスに追加する環境名 env: dev #【共通】静的ウェブサイトホスティングに使用するS3バケット名 bucketName: cfn-acm-s3-edge-cloudfront-20210829000000-hostingbucket1 #【共通】セカンダリS3バケットを作成するスタックをus-east1にデプロイするLambdaカスタムリソースのARN runCfnInUsEast1LambdaARN: 'arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:CustomResourceToDeployCloudformationStack' #【ACM証明書用】ACM証明書を作成するためにus-east1にデプロイするCloudformationスタック名 runCfnInUsEast1StackName4ACM: CfnAcm4CloudFrontDemo #【ACM証明書用】ACM証明書を作成するCloudformationテンプレートを保存しているバケット名 cfnTplS3Bucket4ACM: h-o2k #【ACM証明書用】ACM証明書を作成するCloudformationテンプレートを保存しているバケット以降のオブジェクトキー cfnTplS3Key4ACM: CfnACMCertificate.yml #【ACM証明書用】ACM証明書を発行する対象ドメイン名 customDomainName4ACM: cfn-acm-s3-lambdaedge-cloudfront.h-o2k.com #【ACM証明書用】ACM証明書を発行する対象ドメイン名を管理するRoute53のホストゾーンID hostedZoneId4ACM: XXXXXXXXXXXXXXXXXXXXX #【セカンダリS3用】セカンダリS3バケットを作成するためにus-east1にデプロイするCloudformationスタック名 runCfnInUsEast1StackName4S3BK: Cfn2ndS34CloudFrontDemo #【セカンダリS3用】セカンダリS3バケットを作成するCloudformationテンプレートを保存しているバケット名 cfnTplS3Bucket4S3BK: h-o2k #【セカンダリS3用】セカンダリS3バケットを作成するCloudformationテンプレートを保存しているバケット以降のオブジェクトキー cfnTplS3Key4S3BK: CfnS3OtherRegion.yml #【セカンダリS3用】静的ウェブサイトホスティングのバックアップに使用するセカンダリS3バケット名 bucketName4S3BK: cfn-acm-s3-edge-cloudfront-20210829000000-hostingbucket2 #【Lambda@Edge用】Lambda@Edgeを作成するためにus-east1にデプロイするCloudformationスタック名 runCfnInUsEast1StackName4LambdaEdge: CfnLambdaEdge4CloudFrontDemo #【Lambda@Edge用】Lambda@Edgeを作成するCloudformationテンプレートを保存しているバケット名 cfnTplS3Bucket4LambdaEdge: h-o2k #【Lambda@Edge用】Lambda@Edgeを作成するCloudformationテンプレートを保存しているバケット以降のオブジェクトキー cfnTplS3Key4LambdaEdge: CfnBasicAuthLambdaEdge.yml #【Lambda@Edge用】基本認証をするLambda@Edgeの関数名 basicAuthFuncName4LambdaEdge: BasicAuthWithLambdaEdge #【Lambda@Edge用】基本認証をするLambda@Edgeを関連付けるAmazon CloudFrontのDistribution ID。AWS Secrets Managerシークレットの一意識別で使用する。 #※スタックの初回Create処理でAmazon CloudFrontを作成してから、2回目のUpdate処理で入力する basicAuthCloudFrontDistId4LambdaEdge: XXXXXXXXXXXXX #【Lambda@Edge用】基本認証をするLambda@Edgeで認証するID。AWS Secrets Managerシークレットとして保管する。 basicAuthID4LambdaEdge: Iam #【Lambda@Edge用】基本認証をするLambda@Edgeで認証するパスワード。AWS Secrets Managerシークレットとして保管する。 basicAuthPW4LambdaEdge: Nobody
呼出元AWS Cloudformationテンプレート
OrgSourceOfRunCfnACMS3LambdaEdgeOtherRegion.yml
カスタムリソースの呼出元となるAWS Cloudformationテンプレートの例を記載します。
このテンプレートはAWS Amplify CLIでAmazon S3+Amazon CloudFrontの静的ウェブサイトホスティング環境を作成したときに生成されるJSONテンプレートをYAMLに変換し、ACM証明書、基本認証用Lambda@Edge、セカンダリS3バケットの作成処理および、これらのリソースのCloudFrontへの関連付け処理を追加したものです。
このYAMLファイルに追加した処理やポイントとなる箇所についてコメントを追記しておきます。
AWS Amplify CLIによるAmazon S3+Amazon CloudFrontの作成については次の記事を参照してください。
AWSの静的ウェブサイトホスティングで入門するAWS Amplify(Console、CLI) - 構築編(Amplify CLI)
AWSTemplateFormatVersion: '2010-09-09' Description: 'Hosting and ACM, Secondary S3 Bucket, Lambda@Edge resource stack creation' Parameters: env: #【共通】作成するS3バケットのサフィックスに追加する環境名 Type: String Default: dev bucketName: #【共通】静的ウェブサイトホスティングに使用するS3バケット名 Type: String runCfnInUsEast1LambdaARN: #【共通】ACM証明書、セカンダリS3バケット、Lambda@Edgeを作成するスタックをus-east1にデプロイするLambdaカスタムリソースのARN Type: String runCfnInUsEast1StackName4ACM: #【ACM用】ACM証明書を作成するためにus-east1にデプロイするCloudformationスタック名 Type: String cfnTplS3Bucket4ACM: #【ACM用】ACM証明書を作成するCloudformationテンプレートを保存しているバケット名 Type: String cfnTplS3Key4ACM: #【ACM用】ACM証明書を作成するCloudformationテンプレートを保存しているバケット以降のオブジェクトキー Type: String customDomainName4ACM: #【ACM用】ACM証明書を発行する対象ドメイン名 Type: String hostedZoneId4ACM: #【ACM用】ACM証明書を発行する対象ドメイン名を管理するRoute53のホストゾーンID Type: String runCfnInUsEast1StackName4S3BK: #【セカンダリS3用】セカンダリS3バケットを作成するためにus-east1にデプロイするCloudformationスタック名 Type: String cfnTplS3Bucket4S3BK: #【セカンダリS3用】セカンダリS3バケットを作成するCloudformationテンプレートを保存しているバケット名 Type: String cfnTplS3Key4S3BK: #【セカンダリS3用】セカンダリS3バケットを作成するCloudformationテンプレートを保存しているバケット以降のオブジェクトキー Type: String bucketName4S3BK: #【セカンダリS3用】静的ウェブサイトホスティングのバックアップに使用するセカンダリS3バケット名 Type: String runCfnInUsEast1StackName4LambdaEdge: #【Lambda@Edge用】Lambda@Edgeを作成するためにus-east1にデプロイするCloudformationスタック名 Type: String cfnTplS3Bucket4LambdaEdge: #【Lambda@Edge用】Lambda@Edgeを作成するCloudformationテンプレートを保存しているバケット名 Type: String cfnTplS3Key4LambdaEdge: #【Lambda@Edge用】Lambda@Edgeを作成するCloudformationテンプレートを保存しているバケット以降のオブジェクトキー Type: String basicAuthFuncName4LambdaEdge: #【Lambda@Edge用】基本認証をするLambda@Edgeの関数名 Type: String basicAuthCloudFrontDistId4LambdaEdge: #【Lambda@Edge用】基本認証をするLambda@Edgeを関連付けるAmazon CloudFrontのDistribution ID。AWS Secrets Managerシークレットの一意識別で使用する。 Type: String #※スタックの初回Create処理でAmazon CloudFrontを作成してから、2回目のUpdate処理で入力する basicAuthID4LambdaEdge: #【Lambda@Edge用】基本認証をするLambda@Edgeで認証するID。AWS Secrets Managerシークレットとして保管する。 Type: String basicAuthPW4LambdaEdge: #【Lambda@Edge用】基本認証をするLambda@Edgeで認証するパスワード。AWS Secrets Managerシークレットとして保管する。 Type: String Conditions: ShouldNotCreateEnvResources: 'Fn::Equals': - Ref: env - NONE ShouldCreateBasicAuthLambdaEdge: #入力パラメータにAmazon CloudFrontのDistribution IDがある場合はTrueでLambda@Edgeを作成、ない場合はFalseでLambda@Edge作成をスキップ。 'Fn::Not': ['Fn::Equals':[{Ref: basicAuthCloudFrontDistId4LambdaEdge}, '']] Resources: S3Bucket: Type: 'AWS::S3::Bucket' DependsOn: #カスタムリソースでセカンダリS3バケットが作成されてからクロスリージョンレプリケーションを設定するためDependsOnにカスタムリソースを追加 - RunCfnInUsEast1Func4S3BK - BucketBackupRole #DeletionPolicy: Retain Properties: BucketName: 'Fn::If': - ShouldNotCreateEnvResources - {Ref: bucketName} - {'Fn::Join': ["", [{Ref: bucketName}, '-', {Ref: env}]]} WebsiteConfiguration: IndexDocument: index.html ErrorDocument: index.html CorsConfiguration: CorsRules: - {AllowedHeaders: [Authorization, Content-Length], AllowedMethods: [GET], AllowedOrigins: ['*'], MaxAge: 3000} VersioningConfiguration: Status: Enabled ReplicationConfiguration: #カスタムリソースで作成したセカンダリS3バケットとのクロスリージョンレプリケーション設定をする Role: !GetAtt - BucketBackupRole - Arn Rules: - Destination: Bucket: !GetAtt - RunCfnInUsEast1Func4S3BK - Arn StorageClass: STANDARD Id: ReplicationRule Prefix: '' Status: Enabled BucketBackupRole: #クロスリージョンレプリケーションで使用するIAMロールを設定する Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Statement: - Action: - 'sts:AssumeRole' Effect: Allow Principal: Service: - s3.amazonaws.com BucketBackupPolicy: #クロスリージョンレプリケーションで使用するIAMロールに適用するIAMポリシーを設定する Type: 'AWS::IAM::Policy' DependsOn: - RunCfnInUsEast1Func4S3BK - S3Bucket Properties: PolicyDocument: Statement: - Action: - 's3:GetReplicationConfiguration' - 's3:ListBucket' Effect: Allow Resource: - !Join - '' - - 'arn:aws:s3:::' - !Ref S3Bucket - Action: - 's3:GetObjectVersion' - 's3:GetObjectVersionAcl' Effect: Allow Resource: - !Join - '' - - 'arn:aws:s3:::' - !Ref S3Bucket - /* - Action: - 's3:ReplicateObject' - 's3:ReplicateDelete' Effect: Allow Resource: - !Join - '' - - {'Fn::GetAtt': [RunCfnInUsEast1Func4S3BK, Arn]} - /* PolicyName: BucketBackupPolicy Roles: - !Ref BucketBackupRole PrivateBucketPolicy: Type: 'AWS::S3::BucketPolicy' DependsOn: - OriginAccessIdentity - S3Bucket Properties: PolicyDocument: Id: MyPolicy Version: '2012-10-17' Statement: - {Sid: APIReadForGetBucketObjects, Effect: Allow, Principal: {CanonicalUser: {'Fn::GetAtt': [OriginAccessIdentity, S3CanonicalUserId]}}, Action: 's3:GetObject', Resource: {'Fn::Join': ["", ['arn:aws:s3:::', {Ref: S3Bucket}, '/*']]}} Bucket: Ref: S3Bucket OriginAccessIdentity: Type: 'AWS::CloudFront::CloudFrontOriginAccessIdentity' Properties: CloudFrontOriginAccessIdentityConfig: Comment: CloudFrontOriginAccessIdentityConfig #ACM証明書を発行するために呼び出すカスタムリソース RunCfnInUsEast1Func4ACM: Type: 'Custom::RunCfnInUsEast1Func4ACM' Properties: ServiceToken: Ref: runCfnInUsEast1LambdaARN StackName: Ref: runCfnInUsEast1StackName4ACM CfnTplS3Bucket: Ref: cfnTplS3Bucket4ACM CfnTplS3Key: Ref: cfnTplS3Key4ACM CustomDomainName: Ref: customDomainName4ACM HostedZoneId: Ref: hostedZoneId4ACM #CloudFrontをエイリアスレコードとしてRoute53ホストゾーンに登録するRoute53レコードセット Route53RecordSetGroup: Type: 'AWS::Route53::RecordSetGroup' DependsOn: - CloudFrontDistribution Properties: HostedZoneId: Ref: hostedZoneId4ACM RecordSets: - Name: {Ref: customDomainName4ACM} Type: A #CloudFrontをエイリアスレコードとして登録する場合はエイリアスターゲットのHostedZoneIdが次の固定値となる #参考:https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/quickref-route53.html#w2aac27c21c80c11 AliasTarget: {HostedZoneId: Z2FDTNDATAQYW2, DNSName: {'Fn::GetAtt': [CloudFrontDistribution, DomainName]}} #セカンダリS3バケットを発行するために呼び出すカスタムリソース RunCfnInUsEast1Func4S3BK: DependsOn: #OriginAccessIdentityが作成されてから別リージョンのセカンダリS3バケットを作成する - OriginAccessIdentity Type: 'Custom::RunCfnInUsEast1Func4S3BK' Properties: ServiceToken: Ref: runCfnInUsEast1LambdaARN StackName: Ref: runCfnInUsEast1StackName4S3BK CfnTplS3Bucket: Ref: cfnTplS3Bucket4S3BK CfnTplS3Key: Ref: cfnTplS3Key4S3BK Env: Ref: env BucketName: Ref: bucketName4S3BK S3CanonicalUserId: {'Fn::GetAtt': [OriginAccessIdentity, S3CanonicalUserId]} #Lambda@Edgeを作成するために呼び出すカスタムリソース RunCfnInUsEast1Func4LambdaEdge: Type: 'Custom::RunCfnInUsEast1Func4LambdaEdge' Properties: ServiceToken: Ref: runCfnInUsEast1LambdaARN StackName: Ref: runCfnInUsEast1StackName4LambdaEdge CfnTplS3Bucket: Ref: cfnTplS3Bucket4LambdaEdge CfnTplS3Key: Ref: cfnTplS3Key4LambdaEdge CloudFrontDistId: Ref: basicAuthCloudFrontDistId4LambdaEdge BasicAuthFuncName: Ref: basicAuthFuncName4LambdaEdge BasicAuthID: Ref: basicAuthID4LambdaEdge BasicAuthPW: Ref: basicAuthPW4LambdaEdge IsSkip: ##入力パラメータにAmazon CloudFrontのDistribution IDがない場合はカスタムリソースにIsSkip:trueを送信し、Lambda@Edge作成処理をスキップする。 'Fn::If': - ShouldCreateBasicAuthLambdaEdge - "false" - "true" CloudFrontDistribution: Type: 'AWS::CloudFront::Distribution' DependsOn: - OriginAccessIdentity #カスタムリソースでセカンダリS3バケットが作成されてからCloudFrontに関連付けるためDependsOnにカスタムリソースを追加 - RunCfnInUsEast1Func4S3BK #カスタムリソースでACM証明書が発行されてからCloudFrontに関連付けるためDependsOnにカスタムリソースを追加 - RunCfnInUsEast1Func4ACM - S3Bucket #カスタムリソースで基本認証用Lambda@Edgeバージョンが作成されてからCloudFrontに関連付けるためDependsOnにカスタムリソースを追加 - RunCfnInUsEast1Func4LambdaEdge Properties: DistributionConfig: #CloudFrontのエイリアスにACM証明書を発行したドメイン名を設定する Aliases: - {Ref: customDomainName4ACM} #CloudFrontにカスタムリソースで作成したACM証明書を設定する ViewerCertificate: SslSupportMethod: sni-only MinimumProtocolVersion: TLSv1.2_2021 AcmCertificateArn: {'Fn::GetAtt': [RunCfnInUsEast1Func4ACM, AcmCertificateArn]} HttpVersion: http2 Origins: - {DomainName: {'Fn::GetAtt': [S3Bucket, RegionalDomainName]}, Id: hostingS3Bucket, S3OriginConfig: {OriginAccessIdentity: {'Fn::Join': ["", [origin-access-identity/cloudfront/, {Ref: OriginAccessIdentity}]]}}} #カスタムリソースで作成したus-east-1のS3バケットをオリジンとして登録する - {DomainName: {'Fn::GetAtt': [RunCfnInUsEast1Func4S3BK, RegionalDomainName]}, Id: hostingS3BucketSecondary, S3OriginConfig: {OriginAccessIdentity: {'Fn::Join': ["", [origin-access-identity/cloudfront/, {Ref: OriginAccessIdentity}]]}}} #呼出元リージョンで作成したS3バケットとカスタムリソースで作成したus-east-1のS3バケットでオリジングループを作成する OriginGroups: Items: - FailoverCriteria: StatusCodes: Items: - 500 - 502 - 503 - 504 Quantity: 4 Id: hostingS3BucketGroup Members: Items: - OriginId: hostingS3Bucket - OriginId: hostingS3BucketSecondary Quantity: 2 Quantity: 1 Enabled: 'true' DefaultCacheBehavior: AllowedMethods: [GET, HEAD, OPTIONS] TargetOriginId: hostingS3BucketGroup #オリジングループをターゲットに指定する ForwardedValues: {QueryString: 'false'} ViewerProtocolPolicy: redirect-to-https DefaultTTL: 86400 MaxTTL: 31536000 MinTTL: 60 Compress: true LambdaFunctionAssociations: 'Fn::If': - ShouldCreateBasicAuthLambdaEdge - [{EventType: viewer-request, IncludeBody: 'true', LambdaFunctionARN: {'Fn::GetAtt': [RunCfnInUsEast1Func4LambdaEdge, LambdaFunctionVersionArn]}}] - { Ref: 'AWS::NoValue' } DefaultRootObject: index.html CustomErrorResponses: - {ErrorCachingMinTTL: 300, ErrorCode: 400, ResponseCode: 200, ResponsePagePath: /} - {ErrorCachingMinTTL: 300, ErrorCode: 403, ResponseCode: 200, ResponsePagePath: /} - {ErrorCachingMinTTL: 300, ErrorCode: 404, ResponseCode: 200, ResponsePagePath: /} Outputs: Region: Value: Ref: 'AWS::Region' HostingBucketName: Description: 'Hosting bucket name' Value: Ref: S3Bucket WebsiteURL: Value: 'Fn::GetAtt': - S3Bucket - WebsiteURL Description: 'URL for website hosted on S3' S3BucketSecureURL: Value: 'Fn::Join': - "" - - 'https://' - {'Fn::GetAtt': [S3Bucket, DomainName]} Description: 'Name of S3 bucket to hold website content' CloudFrontDistributionID: Value: Ref: CloudFrontDistribution CloudFrontDomainName: Value: 'Fn::GetAtt': - CloudFrontDistribution - DomainName CloudFrontSecureURL: Value: 'Fn::Join': - "" - - 'https://' - {'Fn::GetAtt': [CloudFrontDistribution, DomainName]} CloudFrontOriginAccessIdentity: Value: Ref: OriginAccessIdentity CustomResourceSkiped: Value: 'Fn::If': - ShouldCreateBasicAuthLambdaEdge - "false" - "true"
呼出元AWS Cloudformationテンプレートからカスタムリソースに渡される引数
前述の呼出元AWS Cloudformationテンプレートのカスタムリソースから渡される引数はそれぞれ、次の記事に記載されたものとなります。
- AWS LambdaカスタムリソースでACM証明書を作成するAWS Cloudformationスタックを別のリージョンにデプロイする
- AWS Lambdaカスタムリソースで基本認証用Lambda@Edgeを作成するAWS Cloudformationスタックを別リージョンにデプロイする
- AWS LambdaカスタムリソースでCloudFrontオリジンフェイルオーバー用S3バケットを作成するAWS Cloudformationスタックを別リージョンにデプロイする
カスタムリソースではこれらのイベントをそれぞれ受けて処理を実行します。
ACM証明書、基本認証用Lambda@Edge、セカンダリS3バケットを作成するAWS Cloudformationスタックをus-east-1にデプロイするAWS Lambdaカスタムリソース
関数名:CustomResourceToDeployCloudformationStack
カスタムリソースはAWS LambdaカスタムリソースでAWS Cloudformationスタックを別リージョンにデプロイするで作成したものを使用します。
このカスタムリソース内でACM証明書、基本認証用Lambda@Edge、セカンダリS3バケットを作成するCloudformationテンプレートをそれぞれ読み込み、呼出元Cloudformationスタックから渡された引数を使用してus-east-1にリソースを作成するCloudformationスタックをデプロイします。
実行したスタックが完了し、リソースが作成されるとARNなど関連付けに必要な情報を呼出元に返却して呼出元Cloudformationスタックで作成されたリソースに関連付けます。
ACM証明書、基本認証用Lambda@Edge、セカンダリS3バケットを作成するAWS Cloudformationテンプレート
ACM証明書、基本認証用Lambda@Edge、セカンダリS3バケットを作成するAWS Cloudformationテンプレートはそれぞれ次の記事で記載のテンプレートを使用しています。
呼出元Cloudformationスタックから、これらのテンプレートをそれぞれ実行するカスタムリソースを呼び出して対応するリソースを作成します。
ACM証明書を作成するAWS Cloudformationテンプレート
ファイル名:CfnACMCertificate.yml
基本認証用Lambda@Edgeを作成するAWS Cloudformationテンプレート
ファイル名:CfnBasicAuthLambdaEdge.yml
セカンダリS3バケットを作成するAWS Cloudformationテンプレート
ファイル名:CfnS3OtherRegion.yml
構築手順
-
AWS Lambda関数「CustomResourceToDeployCloudformationStack」を用意しておく
基本認証用Lambda@Edgeバージョンを作成するAWS Cloudformationスタックをus-east-1にデプロイするAWS Lambdaカスタムリソースを予め準備しておく -
ACM証明書、基本認証用Lambda@Edge、セカンダリS3バケットを作成するCloudformationテンプレートファイルをS3バケットに置く
「CfnACMCertificate.yml」「CfnBasicAuthLambdaEdge.yml」「CfnS3OtherRegion.yml」を作成するAWS CloudformationテンプレートをAmazon S3バケット(前述のパラメータ例では「h-o2k」)に予め配置しておく -
呼出元Cloudformationテンプレートファイル「OrgSourceOfRunCfnACMS3LambdaEdgeOtherRegion.yml 」をAWS Cloudformationで実行する
呼出元CloudformationテンプレートをAWSマネジメントコンソールなどからパラメータを入力の上でAWS Cloudformationスタックとして実行する -
静的ウェブサイトホスティングに使用するS3バケットにコンテンツを追加する
静的ウェブサイトホスティング用S3バケット(前述のパラメータ例では「cfn-acm-s3-edge-cloudfront-20210829000000-hostingbucket1-dev」)にindex.htmlなどコンテンツを追加する。
呼出元Cloudformationスタックで作成したAmazon S3バケットに追加したコンテンツはカスタムリソースで作成したAmazon S3バケットにクロスリージョンレプリケーションされます。
(今回の例では「CfnACMS3LambdaEdge4CloudFrontDemo」とだけ表示されるindex.htmlを追加しました。)
-
初回実行(Create処理でAmazon CloudFrontが作成され、Distribution IDが発行される。カスタムリソースによる基本認証用Lambda@Edgeバージョン作成は実行しない。)
最初の呼出元AWS Cloudformationスタックの実行では「basicAuthCloudFrontDistId4LambdaEdge」にAmazon CloudFrontのDistribution IDを入力しないことで、基本認証用Lambda@Edgeを作成するAWS Cloudformationスタックの作成処理はスキップして、それ以外のリソースを作成します。
■初回実行時(Create)のパラメータ入力例
~省略~ basicAuthCloudFrontDistId4LambdaEdge: #初回時は入力しない(Distribution IDが発行されていないので入力できない) ~省略~
-
2回目実行(Update処理でAmazon CloudFrontのDistribution IDをカスタムリソースに渡し、基本認証用Lambda@Edgeバージョンを作成する。)
最初の呼出元AWS Cloudformationスタックの実行では「basicAuthCloudFrontDistId4LambdaEdge」に初回実行で発行されたAmazon CloudFrontのDistribution IDを入力することで、基本認証用Lambda@Edgeを作成するAWS Cloudformationスタックの作成処理を実行して、基本認証用Lambda@Edgeのバージョンと呼出元のAmazon CloudFrontを関連付けます。
■2回目実行時(Update)のパラメータ入力例
#2回目実行時(Update)はAmazon CloudFrontのDistribution IDを含め、すべてのパラメータを入力 ~省略~ basicAuthCloudFrontDistId4LambdaEdge: XXXXXXXXXXXXX ~省略~
-
結果確認
正しく実行されていればパラメータで指定したACM証明書を発行する対象ドメインにhttpsでアクセスすると基本認証のダイアログが表示され、パラメータで指定した基本認証のIDとパスワードで認証を通過します。
(今回の例では「https://cfn-acm-s3-lambdaedge-cloudfront.h-o2k.com/
」にアクセスすると「CfnACMS3LambdaEdge4CloudFrontDemo」と表示されます。)
また、呼出元Cloudformationスタックで作成したAmazon S3バケットを指すオリジンで5xx系のエラーが発生する場合は、カスタムリソースで作成したAmazon S3バケットを指すオリジンにCloudFrontオリジンフェイルオーバーで参照先が切り替わります。
正しく動作しない場合は呼出元AWS Cloudformationスタックのイベント内容、カスタムリソースでデプロイしたスタックのイベント内容、AWS LambdaカスタムリソースのAmazon CloudWatch Logsの内容、AWS CloudTrailのログから原因を特定して不具合を修正します。
削除手順
今回のAWS CloudformationテンプレートではLambda@EdgeとAmazon CloudFrontの関連付け解除とカスタムリソースの削除の順序制御をしていないため、一度にすべてのスタックを削除することができません。
そのため、削除する場合は次の手順のように関連付けを解除した後、呼出元AWS Cloudformationスタックと基本認証用Lambda@Edgeバージョンを作成したスタックをそれぞれ削除する必要があります。
- 呼出元AWS CloudformationスタックのパラメータでAmazon CloudFrontのDistribution IDを空にしてUpdate処理をする
呼出元AWS Cloudformationスタックのパラメータ「basicAuthCloudFrontDistId4LambdaEdge」を空にしてスタックを更新する。
us-east-1の基本認証用Lambda@Edgeのスタックの削除はCloudFrontとの関連付けがあるので失敗するが、呼び出し元のスタックではAmazon CloudFrontとLambda@Edgeの関連付けが削除される。 - 呼出元AWS Cloudformationスタックを削除する
呼出元AWS Cloudformationスタックと基本認証用Lambda@Edgeのスタックの関連付けが解除されているため、呼出元AWS Cloudformationスタックが削除できる。 - us-east-1で基本認証用Lambda@Edgeバージョンを作成したCloudformationスタックを削除する
呼出元AWS Cloudformationスタックと基本認証用Lambda@Edgeのスタックの関連付けが解除されているため、基本認証用Lambda@Edgeバージョンを作成したCloudformationスタックを単独で削除する。
まとめ
今回は「AWS LambdaカスタムリソースでAWS Cloudformationスタックを別リージョンにデプロイする」の記事で説明したAWS Cloudformationスタックを別リージョンにデプロイするAWS Lambdaカスタムリソースを使用し、ACM証明書、基本認証用Lambda@Edge、セカンダリS3バケットをそれぞれus-east-1で作成して、呼出元AWS Cloudformationスタックで作成したリソースと関連付ける例を紹介しました。
次回はAWS Amplify CLIで作成したAmazon S3+Amazon CloudFrontのホスティング環境に今回の内容を適用して、AWS Amplify Consoleのホスティング環境と同じような環境を作成する例を紹介しようと考えています。