概要
Nested Stackを使うと複数以上のテンプレートを同時に作成できるので開発効率が上がってとても便利です
今回はフロントエンドで使用する
- Amplify
- CloudFront
をNested Stackを使って作成する方法について解説します
前提
- 今回はAmplifyとCloudFrontをNested Stackを使って同時に作成していきます
- ACMをus-east-1に作成済み
- GitHub用のPATを作成済み
Nested Stackとは
CloudFormationテンプレートの中で別のCloudFormationスタックを参照し、デプロイする方法です
ネストされたスタックを使用することで複数以上のテンプレートをより管理、デプロイしやすくなります
テンプレートを保存するS3バケットの作成
Nested Stackを使う際はS3バケット内のテンプレートファイルを参照する必要があります
AWSTemplateFormatVersion: 2010-09-09
Description: "S3 Bucket Factory Settings Stack"
# -------------------------------------
# Metadata
# -------------------------------------
Metadata:
AWS::CloudFormation::Interface:
# パラメータの並び順
ParameterGroups:
- Label:
default: "Project Configuration"
Parameters:
- ProjectName
- Environment
# -------------------------------------
# Input parameters
# -------------------------------------
Parameters:
ProjectName:
Description: "Enter the project name. (ex: my-project)"
Type: String
MinLength: 1
ConstraintDescription: "ProjectName must be enter."
Default: my-project
Environment:
Description: "Select the environment."
Type: String
AllowedValues:
- dev
- stg
- prd
ConstraintDescription: "Environment must be select."
# -------------------------------------
# Resources
# -------------------------------------
Resources:
# -------------------------------------
# S3
# -------------------------------------
# For CloudFormation Templates
CloudFormationTemplatesBucket:
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Type: AWS::S3::Bucket
Properties:
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
BucketName: !Sub cf-templates-${ProjectName}-${Environment}-${AWS::Region}
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
VersioningConfiguration:
Status: Enabled
LifecycleConfiguration:
Rules:
- Id: ExpiresAfter365DaysFor4thOlderVersion
Status: Enabled
NoncurrentVersionExpiration:
NewerNoncurrentVersions: 3
NoncurrentDays: 365
# -------------------------------------
# Output Parameters
# -------------------------------------
Outputs:
CloudFormationTemplatesBucketName:
Value: !Ref CloudFormationTemplatesBucket
CloudFormationTemplatesBucketArn:
Value: !GetAtt CloudFormationTemplatesBucket.Arn
実際に作成してみよう!
Amplifyの作成
Amplify用のStackを作成します
AWSTemplateFormatVersion: 2010-09-09
Description: "Amplify Frontend Hosting Stack"
# -------------------------------------
# Metadata
# -------------------------------------
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: "Project Configuration"
Parameters:
- ProjectName
- Environment
- Label:
default: "Amplify Configuration"
Parameters:
# GitHubへのread write権限を付与するため
- GitHubAccessToken
- GitHubRepoURL
- SourceBranchName
# For Amplify
- AmplifyDomainName
- AmplifySubDomainPrefix
- AmplifyPlatform
- AmplifyBasicUserName
- AmplifyBasicPassword
- APIURL
- FetchCredential
- ALBHeaderAuthName
- ALBHeaderAuthValue
# -------------------------------------
# Parameters
# -------------------------------------
Parameters:
ProjectName:
Description: "Enter the project name. (ex: my-project)"
Type: String
MinLength: 1
ConstraintDescription: "ProjectName must be enter."
Default: my-project
Environment:
Description: "Select the environment."
Type: String
AllowedValues:
- dev
- stg
- prd
ConstraintDescription: "Environment must be select"
GitHubAccessToken:
Description: "Enter the Github personal access token. (ex: ghp_xxxxxxxxxx)"
Type: String
NoEcho: true
GitHubRepoURL:
Description: "Enter the Github repository URL."
Type: String
SourceBranchName:
Description: "Enter the source branch name in GitHub frontend repository. (ex: main)"
Type: String
AmplifyDomainName:
Description: "Enter the custom Amplify domain name. (Alias) (ex: shun-practice.com)"
Type: String
AmplifySubDomainPrefix:
Description: "Enter the custom Amplify domain name subdomain prefix. (ex: amplify)"
Type: String
# SSRを使用する際はWEB_COMPUTEを選択
AmplifyPlatform:
Description: "Select the Amplify app platform. (when using SSR: WEB_COMPUTE)"
Type: String
Default: WEB_COMPUTE
AllowedValues:
- WEB
- WEB_COMPUTE
- WEB_DYNAMIC
AmplifyBasicUserName:
Description: "Enter the Amplify basic auth username."
Type: String
MinLength: 1
MaxLength: 255
ConstraintDescription: "AmplifyBasicUserName must be 1 to 225 characters."
AmplifyBasicPassword:
Description: "Enter the Amplify basic auth password."
Type: String
NoEcho: true
MinLength: 7
MaxLength: 255
ConstraintDescription: "AmplifyBasicPassword must be 7 to 225 characters."
APIURL:
Description: "Enter the API URL for backend. (ex: https://api.shun-practice.com)"
Type: String
FetchCredential:
Description: "Enter the auth settings for API fetch from frontend."
Type: String
Default: include
ALBHeaderAuthName:
Description: "Enter the ALB header auth name. (ex: X-Auth-ALB)"
Type: String
ALBHeaderAuthValue:
Description: "Enter the ALB header auth value."
Type: String
# -------------------------------------
# Resources
# -------------------------------------
Resources:
# -------------------------------------
# Amplify Hosting
# -------------------------------------
# https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-amplify-app.html
AmplifyFrontHosting:
Type: AWS::Amplify::App
Properties:
Name: !Sub ${ProjectName}-${Environment}-front-amplify
# GitHubにアクセスするために必要
AccessToken: !Ref GitHubAccessToken
Repository: !Ref GitHubRepoURL
Platform: !Ref AmplifyPlatform
# Automatically disconnect a branch in Amplify Hosting when you delete a branch from your Git repository
EnableBranchAutoDeletion: true
BasicAuthConfig:
EnableBasicAuth: true
Username: !Ref AmplifyBasicUserName
Password: !Ref AmplifyBasicPassword
IAMServiceRole: !Ref AmplifyServiceRole
# 環境変数
EnvironmentVariables:
- Name: NEXT_PUBLIC_API_BASE_URL
Value: !Ref APIURL
- Name: NEXT_PUBLIC_CREDENTIALS
Value: !Ref FetchCredential
- Name: NEXT_PUBLIC_AUTH_NAME
Value: !Ref ALBHeaderAuthName
- Name: NEXT_PUBLIC_AUTH_VALUE
Value: !Ref ALBHeaderAuthValue
- Name: _DISABLE_L2_CACHE
Value: true
- Name: AMPLIFY_MONOREPO_APP_ROOT
Value: application
BuildSpec: |
version: 1
applications:
- appRoot: application
frontend:
phases:
preBuild:
commands:
- curl https://get.volta.sh | bash
- source ~/.bash_profile
- volta install node
- npm ci
build:
commands:
- npm run build
artifacts:
baseDirectory: .next
files:
- "**/*"
cache:
paths:
- "node_modules/**/*"
Tags:
- Key: Name
Value: !Sub ${ProjectName}-${Environment}-front-amplify
- Key: ProjectName
Value: !Ref ProjectName
- Key: Environment
Value: !Ref Environment
# -------------------------------------
# Amplify Branch
# -------------------------------------
AmplifyBranch:
Type: AWS::Amplify::Branch
Properties:
AppId: !GetAtt AmplifyFrontHosting.AppId
BranchName: !Ref SourceBranchName
EnableAutoBuild: true
EnablePullRequestPreview: false
Framework: Next.js - SSR
Tags:
- Key: ProjectName
Value: !Ref ProjectName
- Key: Environment
Value: !Ref Environment
- Key: BranchName
Value: !Ref SourceBranchName
# -------------------------------------
# Amplify Custom Domain
# -------------------------------------
AmplifyCustomDomain:
DependsOn: AmplifyBranch
Type: AWS::Amplify::Domain
Properties:
AppId: !GetAtt AmplifyFrontHosting.AppId
DomainName: !Ref AmplifyDomainName
SubDomainSettings:
- Prefix: !Ref AmplifySubDomainPrefix
BranchName: !Ref SourceBranchName
# -------------------------------------
# Amplify Service Role
# -------------------------------------
AmplifyServiceRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub AmplifyServiceRoleForCWL-${ProjectName}-${Environment}-front
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: amplify.amazonaws.com
Action:
- sts:AssumeRole
AmplifyServiceRolePolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: !Sub AmplifyAccessForCWL-${ProjectName}-${Environment}-front
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- logs:CreateLogStream
- logs:CreateLogGroup
- logs:DescribeLogGroups
- logs:PutLogEvents
Resource: "*"
Roles:
- Ref: AmplifyServiceRole
# -------------------------------------
# Outputs
# -------------------------------------
Outputs:
AmplifyAppID:
Value: !GetAtt AmplifyFrontHosting.AppId
AmplifyCustomDomainURL:
Value: !Sub ${AmplifySubDomainPrefix}.${AmplifyCustomDomain.DomainName}
AmplifyBasicAuthCredentials:
Value: !Join
- " "
- - Basic
- Fn::Base64: !Sub ${AmplifyBasicUserName}:${AmplifyBasicPassword}
CloudFrontの作成
今回はCloudFrontにbasic認証をかけます
IDはtest
、PWはTest012345678
です
echo -n "test:Test012345678" | base64
dGVzdDpUZXN0MDEyMzQ1Njc4
CloudFront用のStackを作成します
AWSTemplateFormatVersion: 2010-09-09
Description: "CloudFront Stack"
# -------------------------------------
# Mappings
# -------------------------------------
Mappings:
# CachePolicyIdは以下の記事を参照
# https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/using-managed-cache-policies.html
CachePolicyIds:
CachingOptimized:
Id: 658327ea-f89d-4fab-a63d-7e88639e58f6
CachingDisabled:
Id: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad
CachingOptimizedForUncompressedObjects:
Id: b2884449-e4de-46a7-ac36-70bc7f1ddd6d
Elemental-MediaPackage:
Id: 08627262-05a9-4f76-9ded-b50ca2e3a84f
Amplify:
Id: 2e54312d-136d-493c-8eb9-b001f22f67d2
# ResponseHeadersPolicyIdは以下の記事を参照
# https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/using-managed-response-headers-policies.html
ResponseHeadersPolicyIds:
CORS-and-SecurityHeadersPolicy:
Id: e61eb60c-9c35-4d20-a928-2b84e02af89c
CORS-With-Preflight:
Id: 5cc3b908-e619-4b99-88e5-2cf7f45965bd
CORS-with-preflight-and-SecurityHeadersPolicy:
Id: eaab4381-ed33-4a86-88ca-d9558dc6cd63
SecurityHeadersPolicy:
Id: 67f7725c-6f97-4210-82d7-5512b31e9d03
SimpleCORS:
Id: 60669652-455b-4ae9-85a4-c4c02393f86c
# -------------------------------------
# Metadata
# -------------------------------------
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: "Project Configuration"
Parameters:
- ProjectName
- Environment
- Label:
default: "Route 53 Configuration"
Parameters:
- HostZoneID
- DomainName
- Label:
default: "CloudFront Configuration"
Parameters:
- CloudFrontHostZoneID
- ACMPublicCertificateArn
- AmplifyURL
- CachePolicy
- ResponseHeadersPolicy
- WebACLArn
# -------------------------------------
# Parameters
# -------------------------------------
Parameters:
ProjectName:
Description: "Enter the project name (ex: my-project)"
Type: String
MinLength: 1
ConstraintDescription: "ProjectName must be enter"
Default: my-project
Environment:
Description: "Select the environment"
Type: String
AllowedValues: [dev, stg, prd]
ConstraintDescription: "Environment must be select"
HostZoneID:
Description: "Select the Route 53 Hosted Zone ID"
Type: AWS::Route53::HostedZone::Id
DomainName:
Description: "Enter the Route 53 domain name"
Default: shun-practice.com
Type: String
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53-recordset-aliastarget.html#cfn-route53-recordset-aliastarget-hostedzoneid
CloudFrontHostZoneID:
Type: String
Description: "Select the CloudFront hosted zone ID. (fixed value: Z2FDTNDATAQYW2)"
Default: Z2FDTNDATAQYW2
AllowedValues:
- Z2FDTNDATAQYW2
ACMPublicCertificateArn:
Description: "Enter the ACM public certificate ARN for global region"
Type: String
AmplifyURL:
Type: String
Description: "Enter the Amplify URL. (ex: <branch name>.<random number>.amplifyapp.com)"
AuthHeaderValue:
Description: "Enter the encoded Amplify basic auth credentials"
Type: String
CachePolicy:
Description: "Select the CloudFront cache policy"
Type: String
Default: CachingDisabled
AllowedValues:
- CachingOptimized
- CachingDisabled
- CachingOptimizedForUncompressedObjects
- Elemental-MediaPackage
- Amplify
ResponseHeadersPolicy:
Description: "Select the CloudFront response headers policy"
Type: String
Default: SecurityHeadersPolicy
AllowedValues:
- CORS-and-SecurityHeadersPolicy
- CORS-With-Preflight
- CORS-with-preflight-and-SecurityHeadersPolicy
- SecurityHeadersPolicy
- SimpleCORS
WebACLArn:
Description: "Enter the ARN of the WAFv2 to apply to CloudFront (ex: arn:aws:wafv2:us-east-1:012345678910:global/webacl/example/xxxxx)"
Type: String
# -------------------------------------
# Resources
# -------------------------------------
Resources:
# -------------------------------------
# Route 53
# -------------------------------------
CloudFrontAliasRecord:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneId: !Ref HostZoneID
Name: !Ref DomainName
Type: A
# CloudFront用の設定
AliasTarget:
HostedZoneId: !Ref CloudFrontHostZoneID
DNSName: !GetAtt AssetsDistribution.DomainName
# -------------------------------------
# CloudFront Distribution
# -------------------------------------
AssetsDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
- Id: Amplify
DomainName: !Ref AmplifyURL
CustomOriginConfig:
OriginProtocolPolicy: https-only
OriginCustomHeaders:
- HeaderName: Authorization
HeaderValue: !Ref AuthHeaderValue
Enabled: true
Comment: "Amplify Hosting Distribution"
CustomErrorResponses:
- ErrorCachingMinTTL: 0
ErrorCode: 404
ResponseCode: 200
ResponsePagePath: /404/index.html
DefaultCacheBehavior:
CachePolicyId: !FindInMap [CachePolicyIds, !Ref CachePolicy , Id]
ResponseHeadersPolicyId: !FindInMap [ResponseHeadersPolicyIds, !Ref ResponseHeadersPolicy, Id]
FunctionAssociations:
- EventType: viewer-request
FunctionARN: !Ref CloudFrontFunctionForBasicAuth
TargetOriginId: Amplify
ViewerProtocolPolicy: redirect-to-https
HttpVersion: http2
ViewerCertificate:
AcmCertificateArn: !Ref ACMPublicCertificateArn
MinimumProtocolVersion: TLSv1.2_2021
SslSupportMethod: sni-only
# 代替ドメイン名の設定に必要
Aliases:
- !Ref DomainName
IPV6Enabled: false
WebACLId: !Ref WebACLArn
# For CloudFront Functions
# https://dev.classmethod.jp/articles/apply-basic-authentication-password-with-cloudfront-functions/
CloudFrontFunctionForBasicAuth:
Type: AWS::CloudFront::Function
Properties:
Name: !Sub ${ProjectName}-${Environment}-BasicAuth
AutoPublish: true
FunctionCode: |-
function handler(event) {
var request = event.request;
var headers = request.headers;
// echo -n user:pass | base64
var authString = "Basic dGVzdDpUZXN0MDEyMzQ1Njc4";
if (
typeof headers.authorization === "undefined" ||
headers.authorization.value !== authString
) {
return {
statusCode: 401,
statusDescription: "Unauthorized",
headers: {"www-authenticate": {value: "Basic"}}
};
}
return request;
}
FunctionConfig:
Comment: "Function for basic authentication."
Runtime: cloudfront-js-1.0
# -------------------------------------
# Outputs
# -------------------------------------
Outputs:
AssetsDistributionID:
Value: !Ref AssetsDistribution
Nested Stackの作成
CloudFormationではAWS::CloudFormation::Stack
を使って
先ほど作成した子スタック(AmplifyとCloudFrontのスタック)を参照します
参照する際はS3内のURLを指定します
また、Nested Stack作成時に子スタックのパラメータを渡すよう設定します
AWSTemplateFormatVersion: 2010-09-09
Description: "Frontend Stack (CloudFront, Amplify)"
# -------------------------------------
# Metadata
# -------------------------------------
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: "CloudFormation Configuration"
Parameters:
- CloudFrontTemplateURL
- AmplifyTemplateURL
- Label:
default: "Project Configuration"
Parameters:
- ProjectName
- Environment
- Label:
default: "CloudFront Configuration"
Parameters:
- HostZoneID
- DomainName
- ACMPublicCertificateArn
- CachePolicy
- ResponseHeadersPolicy
- WebACLArn
- Label:
default: "Amplify Configuration"
Parameters:
- GitHubAccessToken
- GitHubRepoURL
- SourceBranchName
- AmplifySubDomainPrefix
- AmplifyPlatform
- AmplifyBasicUserName
- AmplifyBasicPassword
- APIURL
- FetchCredential
- ALBHeaderAuthName
- ALBHeaderAuthValue
# -------------------------------------
# Parameters
# -------------------------------------
Parameters:
# -------------------------------------
# CloudFormation
# -------------------------------------
CloudFrontTemplateURL:
Description: "Enter the CloudFront template object URL in S3 bucket"
Type: String
AmplifyTemplateURL:
Description: "Enter the Amplify template object URL in S3 bucket"
Type: String
# -------------------------------------
# Project
# -------------------------------------
ProjectName:
Description: "Enter the project name. (ex: my-project)"
Type: String
MinLength: 1
ConstraintDescription: "ProjectName must be enter."
Default: my-project
Environment:
Description: "Select the environment."
Type: String
AllowedValues:
- dev
- stg
- prd
ConstraintDescription: "Environment must be select"
# -------------------------------------
# CloudFront
# -------------------------------------
HostZoneID:
Description: "Select the Route 53 Hosted Zone ID"
Type: AWS::Route53::HostedZone::Id
DomainName:
Description: "Enter the cloudfront domain name (Alias) (ex: shun-practice.com)"
Type: String
ACMPublicCertificateArn:
Description: "Enter the ACM public certificate ARN for global region"
Type: String
CachePolicy:
Description: "Select the CloudFront cache policy"
Type: String
Default: CachingDisabled
AllowedValues:
- CachingOptimized
- CachingDisabled
- CachingOptimizedForUncompressedObjects
- Elemental-MediaPackage
- Amplify
ResponseHeadersPolicy:
Description: "Select the CloudFront response headers policy"
Type: String
Default: SecurityHeadersPolicy
AllowedValues:
- CORS-and-SecurityHeadersPolicy
- CORS-With-Preflight
- CORS-with-preflight-and-SecurityHeadersPolicy
- SecurityHeadersPolicy
- SimpleCORS
WebACLArn:
Description: "Enter the ARN of the WAFv2 to apply to CloudFront. (ex: arn:aws:wafv2:us-east-1:012345678910:global/webacl/example/xxxxx)"
Type: String
# -------------------------------------
# Amplify
# -------------------------------------
GitHubAccessToken:
Description: "Enter the Github personal access token. (ex: ghp_xxxxxxxxxx)"
Type: String
NoEcho: true
GitHubRepoURL:
Description: "Enter the Github repository URL."
Type: String
SourceBranchName:
Description: "Enter the source branch name in GitHub frontend repository. (ex: main)"
Type: String
AmplifyDomainName:
Description: "Enter the custom Amplify domain name. (Alias) (ex: shun-practice.com)"
Type: String
AmplifySubDomainPrefix:
Description: "Enter the custom Amplify domain name subdomain prefix. (ex: amplify)"
Type: String
# SSRを使用する際はWEB_COMPUTEを選択
AmplifyPlatform:
Description: "Select the Amplify app platform. (when using SSR: WEB_COMPUTE)"
Type: String
Default: WEB_COMPUTE
AllowedValues:
- WEB
- WEB_COMPUTE
- WEB_DYNAMIC
AmplifyBasicUserName:
Description: "Enter the Amplify basic auth username"
Type: String
MinLength: 1
MaxLength: 255
ConstraintDescription: "AmplifyBasicUserName must be 1 to 225 characters"
AmplifyBasicPassword:
Description: "Enter the Amplify basic auth password"
Type: String
NoEcho: true
MinLength: 7
MaxLength: 255
ConstraintDescription: "AmplifyBasicPassword must be 7 to 225 characters"
APIURL:
Description: "Enter the API URL for backend. (ex: https://api.shun-practice.com)"
Type: String
FetchCredential:
Description: "Enter the auth settings for API fetch from frontend."
Type: String
Default: include
ALBHeaderAuthName:
Description: "Enter the ALB header auth name. (ex: X-Auth-ALB)"
Type: String
ALBHeaderAuthValue:
Description: "Enter the ALB header auth value."
Type: String
Resources:
# -------------------------------------
# CloudFront Stack
# -------------------------------------
CloudFrontStack:
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Ref CloudFrontTemplateURL
Parameters:
ProjectName: !Ref ProjectName
Environment: !Ref Environment
HostZoneID: !Ref HostZoneID
DomainName: !Ref DomainName
ACMPublicCertificateArn: !Ref ACMPublicCertificateArn
AmplifyURL: !GetAtt AmplifyStack.Outputs.AmplifyCustomDomainURL
AuthHeaderValue: !GetAtt AmplifyStack.Outputs.AmplifyBasicAuthCredentials
CachePolicy: !Ref CachePolicy
ResponseHeadersPolicy: !Ref ResponseHeadersPolicy
WebACLArn: !Ref WebACLArn
# -------------------------------------
# Amplify Stack
# -------------------------------------
AmplifyStack:
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Ref AmplifyTemplateURL
Parameters:
ProjectName: !Ref ProjectName
Environment: !Ref Environment
GitHubAccessToken: !Ref GitHubAccessToken
GitHubRepoURL: !Ref GitHubRepoURL
SourceBranchName: !Ref SourceBranchName
AmplifyDomainName: !Ref DomainName
AmplifySubDomainPrefix: !Ref AmplifySubDomainPrefix
AmplifyPlatform: !Ref AmplifyPlatform
AmplifyBasicUserName: !Ref AmplifyBasicUserName
AmplifyBasicPassword: !Ref AmplifyBasicPassword
APIURL: !Ref APIURL
FetchCredential: !Ref FetchCredential
ALBHeaderAuthName: !Ref ALBHeaderAuthName
ALBHeaderAuthValue: !Ref ALBHeaderAuthValue
実際に作成してみよう!
テンプレートを先ほど作成したS3へアップロードします
コンソールからスタックを作成します
URLの値はS3内のyamlファイルのプロバティにあるオブジェクトURLを指定します
以下のようにAmplifyとCloudFrontのスタックがまとめて作成されたら成功です
参考