小西秀和です。
前回「AWS LambdaカスタムリソースでAWS Cloudformationスタックを別リージョンにデプロイする」の記事でAWS Cloudformationスタックを別リージョンにデプロイするAWS Lambdaカスタムリソースの実装例を説明しました。
今回はその記事の続編として、前回記事で作成したカスタムリソースを使用して実際にAWS Certificate Manager(ACM)証明書をデプロイする方法を紹介します。
AWS Cloudformationスタックを別リージョンにデプロイするAWS Lambdaカスタムリソースについては元記事を参照してください。
※本記事および当執筆者のその他の記事で掲載されているソースコードは自主研究活動の一貫として作成したものであり、動作を保証するものではありません。使用する場合は自己責任でお願い致します。また、予告なく修正することもありますのでご了承ください。
本記事で試す内容の概要図
今回は次の内容をAWS Cloudformationとカスタムリソースを中心に試しています。
- 呼出元Cloudformationスタックはカスタムリソースを呼び出し、ACM証明書をCloudformationスタックでus-east-1に作成してARNを受け取る
- 呼出元CloudformationスタックはAmazon S3をオリジンとするAmazon CloudFrontをACM証明書のARNと関連付けて作成し、Route53レコードセットに登録してACM証明書を発行したドメインでアクセスできるようにする
各ファイル(テンプレート、関数)の名称設定
AWS CloudformationテンプレートやAWS Lambdaカスタムリソースそれぞれが一意に必要なものを識別して連携できれば各ファイルの名称は任意ですが、説明のために次のように各ファイルの名称を設定します。
- 呼出元AWS Cloudformationテンプレート
⇒ OrgSourceOfRunCfnACM.yml - us-east-1にACM証明書を作成するAWS CloudformationスタックをデプロイするAWS Lambdaカスタムリソース
⇒ CustomResourceToDeployCloudformationStack - ACM証明書を作成するAWS Cloudformationテンプレート
⇒ CfnACMCertificate.yml
各ファイルやパラメータの例
各ファイルやパラメータの例を記載していきます。
特に入力パラメータに関しては入力形式を知っていただくための例なので、使用する場合は各パラメータを要件に合わせて設定する必要があります。
呼出元AWS Cloudformationテンプレートへの入力パラメータ例
後述するカスタムリソースの呼出元となるAWS Cloudformationテンプレートに入力するパラメータの例です。
入力値は例なので使用する場合は各パラメータを要件に合わせて設定する必要があります。
次にYAML形式にコメントする形で説明しています。
これらのパラメータはAWSマネジメントコンソールから呼出元AWS Cloudformationテンプレートを実行する場合は、各パラメータを手動入力する必要があります。
#作成するS3バケットのサフィックスに追加する環境名 env: dev #静的ウェブサイトホスティングに使用するS3バケット名 bucketName: cfnacm4cloudfront-20210822000000-hostingbucket #ACM証明書を作成するスタックをus-east1にデプロイするLambdaカスタムリソースのARN runCfnInUsEast1LambdaARN: 'arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:CustomResourceToDeployCloudformationStack' #ACM証明書を作成するためにus-east1にデプロイするCloudformationスタック名 runCfnInUsEast1StackName4ACM: CfnAcm4CloudFrontDemo #ACM証明書を作成するCloudformationテンプレートを保存しているバケット名 cfnTplS3Bucket4ACM: h-o2k #ACM証明書を作成するCloudformationテンプレートを保存しているバケット以降のオブジェクトキー cfnTplS3Key4ACM: CfnACMCertificate.yml #ACM証明書を発行する対象ドメイン名 customDomainName4ACM: cfn-acm4cf.h-o2k.com #ACM証明書を発行する対象ドメイン名を管理するRoute53のホストゾーンID hostedZoneId4ACM: XXXXXXXXXXXXXXXXXXXXX
呼出元AWS Cloudformationテンプレート
ファイル名:OrgSourceOfRunCfnACM.yml
カスタムリソースの呼出元となるAWS Cloudformationテンプレートの例を記載します。
このテンプレートはAWS Amplify CLIでAmazon S3+Amazon CloudFrontの静的ウェブサイトホスティング環境を作成したときに生成されるJSONテンプレートをYAMLに変換し、カスタムリソースによるACM証明書作成処理とRoute53およびCloudFrontへの関連付け処理を追加したものです。
このYAMLファイルに追加した処理やポイントとなる箇所についてコメントを追記しておきます。
AWS Amplify CLIによるAmazon S3+Amazon CloudFrontの作成については次の記事を参照してください。
AWSの静的ウェブサイトホスティングで入門するAWS Amplify(Console、CLI) - 構築編(Amplify CLI)
AWSTemplateFormatVersion: '2010-09-09' Description: 'Hosting and ACM Certificate resource stack creation' Parameters: env: #作成するS3バケットのサフィックスに追加する環境名 Type: String bucketName: #静的ウェブサイトホスティングに使用するS3バケット名 Type: String runCfnInUsEast1LambdaARN: #ACM証明書を作成するスタックをus-east1にデプロイするLambdaカスタムリソースのARN Type: String runCfnInUsEast1StackName4ACM: #ACM証明書を作成するためにus-east1にデプロイするCloudformationスタック名 Type: String cfnTplS3Bucket4ACM: #ACM証明書を作成するCloudformationテンプレートを保存しているバケット名 Type: String cfnTplS3Key4ACM: #ACM証明書を作成するCloudformationテンプレートを保存しているバケット以降のオブジェクトキー Type: String customDomainName4ACM: #ACM証明書を発行する対象ドメイン名 Type: String hostedZoneId4ACM: #ACM証明書を発行する対象ドメイン名を管理するRoute53のホストゾーンID Type: String Conditions: ShouldNotCreateEnvResources: 'Fn::Equals': - Ref: env - NONE Resources: S3Bucket: Type: 'AWS::S3::Bucket' #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} PrivateBucketPolicy: Type: 'AWS::S3::BucketPolicy' DependsOn: OriginAccessIdentity 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]}} CloudFrontDistribution: Type: 'AWS::CloudFront::Distribution' DependsOn: #カスタムリソースでACM証明書が発行されてからCloudFrontに関連付けるためDependsOnにカスタムリソースを追加 - RunCfnInUsEast1Func4ACM - S3Bucket - OriginAccessIdentity 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}]]}}} Enabled: 'true' DefaultCacheBehavior: AllowedMethods: [DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT] TargetOriginId: hostingS3Bucket ForwardedValues: {QueryString: 'false'} ViewerProtocolPolicy: redirect-to-https DefaultTTL: 86400 MaxTTL: 31536000 MinTTL: 60 Compress: true 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
呼出元AWS Cloudformationテンプレートからカスタムリソースに渡される引数
前述の呼出元AWS Cloudformationテンプレートのカスタムリソース(RunCfnInUsEast1Func4ACM)から渡される引数は以下になります。
StackName、cfnTplS3Bucket、cfnTplS3Key、CustomDomainName、HostedZoneId
StackName:ACM証明書などリソースを作成するCloudformationスタックの名称
CfnTplS3Bucket:ACM証明書などリソースを作成するCloudformationテンプレートがあるS3バケット
CfnTplS3Key:ACM証明書などリソースを作成するCloudformationテンプレートがあるS3バケット配下のオブジェクトキー
CustomDomainName:証明書発行対象のサブドメイン
HostedZoneId:証明書発行対象のサブドメインを管理するRoute53のホストゾーンID
これらを設定した上で呼出元AWS Cloudformationテンプレートからカスタムリソースに渡るイベントのフォーマット例を次に示します。
{ "RequestType": "Create", "ResponseURL": "http://pre-signed-S3-url-for-response", "StackId": "arn:aws:cloudformation:ap-northeast-1:123456789012:stack/stack-name/guid", "RequestId": "unique id for this create request", "ResourceType": "Custom::TestResource", "LogicalResourceId": "MyTestResource", "ResourceProperties": { "ServiceToken": "arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:CustomResourceToDeployCloudformationStack", "StackName": "CfnAcm4CloudFrontDemo", "CfnTplS3Bucket": "h-o2k", "CfnTplS3Key": "CfnACMCertificate.yml", "CustomDomainName": "cfn-acm4cf.h-o2k.com", "HostedZoneId": "XXXXXXXXXXXXXXXXXXXXX" } }
カスタムリソースではこのイベントを受けて処理を実行します。
ACM証明書を作成するAWS Cloudformationスタックをus-east-1にデプロイするAWS Lambdaカスタムリソース
関数名:CustomResourceToDeployCloudformationStack
カスタムリソースはAWS LambdaカスタムリソースでAWS Cloudformationスタックを別リージョンにデプロイするで作成したものを使用します。
このカスタムリソース内でAmazon S3バケットに保存されているACM証明書を作成するCloudformationテンプレートを読み込み、呼出元Cloudformationスタックから渡された引数を使用してus-east-1に証明書を作成するCloudformationスタックをデプロイします。
実行したスタックが完了し、証明書が作成されるとそのARNを呼出元に返却して呼出元で作成しているAWS CloudFrontに証明書を関連付けます。
ACM証明書を作成するAWS Cloudformationテンプレート
ファイル名:CfnACMCertificate.yml
ACM証明書を作成し、Amazon Route53のホストゾーンでドメイン認証までを実施するAWS Cloudformationテンプレートは次のようになります。
これをS3バケットに保存しておき、カスタムリソースから読み込んでAWS Cloudformationスタックをus-east-1にデプロイします。
AWSTemplateFormatVersion: '2010-09-09' Description: 'Stack creation of CertificateManager Certificate' Parameters: CustomDomainName: #ACM証明書を発行する対象のカスタムドメイン名 Type: String HostedZoneId: #ACM証明書を発行する対象のカスタムドメインを管理しているAmazon Route53ホストゾーンID Type: String Resources: CertificateManagerCertificate: Type: 'AWS::CertificateManager::Certificate' Properties: DomainName: Ref: CustomDomainName DomainValidationOptions: - DomainName: {Ref: CustomDomainName} HostedZoneId: {Ref: HostedZoneId} ValidationMethod: DNS #カスタムドメインの検証はRoute53のDNSを使用する方法で実施する Outputs: Region: Value: Ref: 'AWS::Region' AcmCertificateArn: Value: Ref: CertificateManagerCertificate #返却値はACM証明書のARN
構築手順
- AWS Lambda関数「CustomResourceToDeployCloudformationStack」を用意しておく
ACM証明書を作成するAWS Cloudformationスタックをus-east-1にデプロイするAWS Lambdaカスタムリソースを予め準備しておく - ACM証明書を作成するCloudformationテンプレートファイル「CfnACMCertificate.yml」をS3バケットに置く
ACM証明書を作成するAWS CloudformationテンプレートをAmazon S3バケット(前述のパラメータ例では「h-o2k」)に予め配置しておく - 呼出元Cloudformationテンプレートファイル「OrgSourceOfRunCfnACM.yml 」をAWS Cloudformationで実行する
呼出元CloudformationテンプレートをAWSマネジメントコンソールなどからパラメータを入力の上でAWS Cloudformationスタックとして実行する - 静的ウェブサイトホスティングに使用するS3バケットにコンテンツを追加する
静的ウェブサイトホスティング用S3バケット(前述のパラメータ例では「cfnacm4cloudfront-20210822000000-hostingbucket-dev」)にindex.htmlなどコンテンツを追加する。
(今回の例では「CfnAcm4CloudFrontDemo」とだけ表示されるindex.htmlを追加しました。) - 結果確認
正しく実行されていればパラメータで指定したACM証明書を発行する対象ドメインにhttpsでアクセスできます。
(今回の例では「https://cfn-acm4cf.h-o2k.com/
」にアクセスすると「CfnAcm4CloudFrontDemo」と表示されます。)
正しく動作しない場合は呼出元AWS Cloudformationスタックのイベント内容、カスタムリソースでデプロイしたスタックのイベント内容、AWS LambdaカスタムリソースのAmazon CloudWatch Logsの内容、AWS CloudTrailのログから原因を特定して不具合を修正します。
参考:
What is AWS CloudFormation? - AWS CloudFormation
Tech Blog with related articles referenced
まとめ
今回は「AWS LambdaカスタムリソースでAWS Cloudformationスタックを別リージョンにデプロイする」の記事で説明したAWS Cloudformationスタックを別リージョンにデプロイするAWS Lambdaカスタムリソースを使用し、ACM証明書をus-east-1で発行して呼出元AWS Cloudformationスタックで管理しているAmazon CloudFrontとRoute53レコードセットに関連付ける例を紹介しました。
次回は同様にカスタムリソースを使用してLambda@Edgeによる基本認証をAmazon CloudFrontに関連付ける例を紹介しようと考えています。