CDK synthテンプレートでECSアプリをAWSコンソールからデプロイしてみた ― つまずきポイントと解決策まとめ

CDK synthテンプレートでECSアプリをAWSコンソールからデプロイしてみた ― つまずきポイントと解決策まとめ

CDK synthテンプレートをCloudFormationコンソールからデプロイする際に遭遇した、ECRクロスアカウントアクセス、DNS設定、Cognito認証などのつまずきポイントとその解決策をまとめました。
2026.07.30

はじめに

「社内で運用しているECSアプリを、自分の個人AWSアカウントにもデプロイしたい」——そんな動機でデプロイ作業を始めたところ、多くのつまずきポイントがありました。

CDK bootstrapを省略してCloudFormationコンソールから直接デプロイする方法、ECRのクロスアカウントアクセス、DNS設定、Cognito認証の構成まで、一連の作業で遭遇したトラブルとその解決策をまとめます。

前提・環境

  • デプロイ対象: AI Starter
  • IaC: AWS CDK(TypeScript)
  • コンテナイメージ: 別のAWSアカウント(ソースアカウント)のECRリポジトリに保管
  • デプロイ先: 個人AWSアカウント
  • リージョン: ap-northeast-1

全体の流れ

deploy-ecs-app-cloudformation-console-full-guide-deploy-flow

事前準備: VPC・ACM証明書・ドメインの用意

CDKスタックはVPCやACM証明書を自前で作成せず、既存リソースのIDをパラメータとして受け取る設計になっています。そのため、CloudFormationスタックをデプロイする前に以下のリソースを手動で用意する必要があります。

VPCの作成

AWSコンソールの「VPC」から新規作成します。今回はテスト用途なので「VPC など」を選択し、以下の構成で作成しました。

項目 設定値
名前タグ 任意(例: ai-starter
IPv4 CIDRブロック 10.0.0.0/16
アベイラビリティーゾーン数 2
パブリックサブネット数 2
プライベートサブネット数 2
NATゲートウェイ なし(コスト削減のため、Publicモードで利用)

SCR-20260727-juer

作成後、CloudFormationのパラメータに入力するため、以下のIDをメモしておきます。

SCR-20260727-jvzg (1)

  • VPC ID(例: vpc-0abc1234def56789
  • パブリックサブネットID × 2(例: subnet-0abc..., subnet-0def...
  • プライベートサブネットID × 2(同上)

ドメインの取得

Route 53で独自ドメインを取得します。.clickなどの安価なTLD(トップレベルドメイン)を選べば、年間数ドルで済みます。ドメインの登録は数分〜数十分で完了します。

SCR-20260727-kspj
ドメインを登録します。

SCR-20260727-ktto (1)
.click は 3ドル/年 で一番安かったです。

SCR-20260727-kuwv (1)
チェックアウトします。

ACM証明書の作成

ALBでHTTPSを有効にするため、AWS Certificate Manager(ACM)で証明書を作成します。Route 53でドメインを管理していれば、DNS検証で「Route 53でレコードを作成」ボタンをクリックするだけで検証が完了し、証明書が発行されます。

SCR-20260727-jwgc
証明書をリクエストします。

SCR-20260727-jwid
パブリック証明書をリクエストします。

SCR-20260727-jwyl

SCR-20260727-lafz (1)
先ほど登録したドメイン + サブドメインを登録します。今回の場合 stg でサブドメインを登録しました。

SCR-20260727-lakf (1)
「Route 53 でレコードを作成」 を選択します。

SCR-20260727-laky (2)

証明書のARNに含まれるID部分を、CloudFormationのパラメータCertificateIdに使用します。

SCR-20260727-lard (1)

CDK synthテンプレートをCloudFormationコンソールからデプロイする

1. テンプレートの生成

pnpm cdk synth > template.yaml

2. CloudFormationコンソールでスタックを作成

AWSコンソールで「CloudFormation」を開き、以下の手順でスタックを作成します。

  1. 「スタックの作成」→「新しいリソースを使用(標準)」を選択
    SCR-20260727-lcvp (1)

  2. 「テンプレートファイルのアップロード」で、先ほど生成したtemplate.yamlをアップロード
    SCR-20260727-ldgb (1)

  3. スタック名を入力(例: cmais-prd-ai-starter-stack
    SCR-20260727-lgox (1)

3. パラメータの入力

事前準備で用意したリソースのIDを入力します。

パラメータ 入力する値
VpcId VPC ID(例: vpc-0abc1234def56789
LBSubnetIds パブリックサブネットID1 / 2
ServiceSubnetIds パブリックサブネットID1 / 2
CertificateId ACM証明書のID(ARNの末尾部分)
NetworkType Public(NATゲートウェイなしの場合)または Private

NetworkTypeは、ALBをパブリックサブネットに配置してインターネットから直接アクセスする場合はPublic、プライベートサブネットに配置する場合はPrivateを選択します。テスト用途でNATゲートウェイを作成していない場合はPublicを選びましょう。

4. スタック作成の実行

残りのオプションはデフォルトのまま「次へ」を進め、最後に「AWS CloudFormation によって IAM リソースが作成される場合があることを承認します。」にチェックを入れて「送信」をクリックします。

SCR-20260727-lhay

スタックの作成には10〜15分程度かかります。ステータスがCREATE_COMPLETEになれば成功です。

スタック作成に失敗した場合

スタック作成に失敗した場合(ROLLBACK_COMPLETECREATE_FAILED)、そのスタックを削除してから再作成する必要があります。パラメータを修正して同じスタック名で再度作成してください。

ecr:SetRepositoryPolicyの権限が必要です。権限が不足した場合は、組織のセキュリティ管理者に相談しましょう。

ECRとコンテナイメージpullの仕組み

ここで「レジストリ」「イメージ」「アーティファクト」といった用語を整理しておきます。

用語 意味
コンテナイメージ アプリケーション + ランタイムをパッケージ化したもの(zipのようなイメージ)
レジストリ イメージを保管・配布するサービス(Docker Hub、GitHub Container Registry、ECRなど)
リポジトリ レジストリ内のイメージ名前空間(GitHubリポジトリに相当)
アーティファクト ビルド成果物の総称。コンテナイメージはアーティファクトの一種

Amazon ECR(Elastic Container Registry) は、AWSが提供するマネージドコンテナレジストリです。GitHubがソースコードを管理するように、ECRはコンテナイメージを管理します。ECSタスクは起動時にECRからdocker pullでイメージを取得し、そのイメージからコンテナを起動します。

2層のアクセス制御

ECRへのアクセスは以下の2層で制御されています。

  1. IAM Task Execution Role(pull側): ECSタスクに付与されるIAMロール。ecr:BatchGetImageecr:GetDownloadUrlForLayerなどの権限が必要です。CloudFormationスタックが自動で作成するため、通常は意識する必要がありません。

  2. ECR Resource-based Policy(レジストリ側): ECRリポジトリ自体に設定するポリシー。どのAWSアカウントがpullできるかを制御します。クロスアカウントの場合、ここにデプロイ先アカウントのIDを明示的に追加する必要があります。

deploy-ecs-app-cloudformation-console-full-guide-ecr-pull-flow

今回のケースでは、IAMロール(1層目)はCloudFormationが自動作成済みでしたが、ECRリポジトリのResource-based Policy(2層目)に個人アカウントIDが未登録だったため、イメージのpullが拒否されました。

DNS・ALB・HTTPS設定

DNSレコードとは

DNSレコードとは、ドメイン名とその送り先を紐付ける「対応表のエントリ」です。すべてのドメインにはネームサーバー(DNSサーバー)があり、この対応表を管理しています。Route 53はAWSのネームサーバーですが、他のサービス(Cloudflare、GoDaddyなど)でドメインを管理している場合は、そちらのDNS設定画面で同様のレコードを作成します。

主要なレコードタイプ:

レコードタイプ 紐付け先 用途
A IPアドレス stg.example.click203.0.113.50
CNAME 別のホスト名 stg.example.clickmy-alb-123.elb.amazonaws.com
AAAA IPv6アドレス Aレコードと同じ役割のIPv6版
MX メールサーバー メールの送信先を指定
TXT 任意のテキスト ドメイン検証(ACMのDNS検証など)に使用

今回はALBへの接続なので、より効率的なAレコード(エイリアス)を使用しています。

ALBのDNSレコード設定

CloudFormationスタック作成後、ALBは作成されますがDNSレコードは自動で作られません。Route 53で**Aレコード(エイリアス)**をALBに向けて手動で作成する必要があります。

  1. スタック > スタック詳細から LoadBalancerDNS の値をメモします。
    SCR-20260727-nexa (1)

  2. Route 53 > 先ほど登録したドメイン > 「レコードを作成」を選択します。
    SCR-20260727-nfji (1)

  3. レコードのパラメータを入力します。

パラメータ 入力する値
レコード名 stg(前指定したサブドメイン)
レコードタイプ A
トラフィックのルーティング先 Application Load Balancer と Classic Load Balancer へのエイリアス
ロードバランサー dualstack.<前メモした LoadBalancerDNS の値>

SCR-20260727-ngcx (2)

  1. 「レコードを作成」を選択

これで設定したドメインからアプリケーションにアクセスできるようになります。

Cognito + Secrets Managerの設定

deploy-ecs-app-cloudformation-console-full-guide-troubleshooting

問題: すべてのルートで404が返る

デプロイ後、/healthcheckは正常に200を返すのに、/login/など他のルートがすべて404になる現象が発生しました。

原因の調査

アプリケーションのコードを調査したところ、サーバーは起動時にSecrets Managerからシークレットを読み込み、OIDC認証の設定を初期化します。この初期化に失敗すると、サーバーはヘルスチェック専用モードにフォールバックし、/healthcheck以外のルートは登録されません。

具体的には、auth-configシークレットのissuerフィールドが空文字列だったため、openid-clientライブラリがTypeError: Cannot read properties of nullを投げていました。

Cognitoユーザープールの作成

OIDC認証のためにCognitoユーザープールを作成しました。

  1. Amazon Cognito > ユーザプールを作成
    SCR-20260727-nwij

  2. アプリケーションを設定します。
    SCR-20260727-nxdb

  3. リターンURLを設定します。

項目 設定値
アプリケーションタイプ 従来のウェブアプリケーション
アプリケーション名 任意
サインインオプション Eメール
リターンURL https://YOUR_DOMAIN/auth/callback
許可されたスコープ email, openid, profile

SCR-20260727-nxlu (1)

ユーザの登録

  1. Amazon Cognito > ユーザプール > ユーザーを作成
    SCR-20260727-nzbe

  2. ユーザー(自分)宛に招待します。

項目 設定値
招待メッセージ Eメールで招待を送信
E メールアドレス <自身のE メールアドレス>
サインインオプション Eメール
仮パスワード パスワードの生成

SCR-20260727-nzjo (1)

  1. 仮パスワードを受信します。
    SCR-20260727-ofad (1)

scopeの設定ミス

Cognitoのアプリクライアント作成時、デフォルトのスコープにphoneが含まれていました。アプリケーションが要求するスコープ(email openid profile)と一致しないため、ログイン時にinvalid_scopeエラーが発生しました。

aws cognito-idp update-user-pool-client \
  --user-pool-id ap-northeast-1_XXXXXXX \
  --client-id YOUR_CLIENT_ID \
  --allowed-o-auth-scopes email openid profile \
  --allowed-o-auth-flows code \
  --allowed-o-auth-flows-user-pool-client \
  --supported-identity-providers COGNITO \
  --callback-urls '["https://YOUR_DOMAIN/auth/callback"]'

Secrets Managerの設定

アプリケーションが参照する主要なシークレット:

シークレット名 必須フィールド 説明
auth-config issuer, client_id, client_secret OIDC認証設定
aws bedrock_region Bedrock利用リージョン
customize title アプリタイトル等のカスタマイズ

auth-configissuerフィールドには、Cognitoユーザープールのissuer URLを設定します:

https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_XXXXXXX

管理画面でアシスタントを有効化する

ここまで来れば、ログインできるようになります。

SCR-20260727-okyg (1)
メールを入力します。

SCR-20260727-olcv
仮パスワードを入力します。

SCR-20260727-olfc
新しいパスワードを設定します。

問題: ログイン後「現在設定中のため、しばらくしてからブラウザをリロードしてください」と表示される

Cognito認証が通りログインできるようになっても、アプリのトップページに「設定中」メッセージが表示されて先に進めませんでした。

SCR-20260727-olhb

原因

このアプリケーションでは、利用可能なAIアシスタント(モデル)が1つも有効になっていない場合、チャット画面に遷移できず「設定中」のフォールバック画面が表示されます。デフォルトではすべてのアシスタントが無効(enabled: false)になっており、管理画面から手動で有効化する必要があります。

解決手順

1. DynamoDBでユーザーに管理者権限を付与

管理画面にアクセスするには、ユーザーテーブルで自分のユーザーのgroupAIS:adminに設定する必要があります。

aws dynamodb put-item \
  --table-name YOUR_PREFIX-user \
  --item '{"id":{"S":"your-email@example.com"},"group":{"S":"AIS:admin"}}'

2. 管理画面でアシスタントを有効化

https://YOUR_DOMAIN/admin/assistantsにアクセスし、使用したいモデル(例: Claude Sonnet 4.6)を有効にします。

SCR-20260727-plva
管理画面から「アシスタント」を選択します。

SCR-20260727-plxw
任意モデル(例:Claude Sonnet 4.6)を有効化します。

3. ブラウザをリロード

アシスタントを有効化したら、メインアプリのページをリロードするとチャット画面に遷移できます。

SCR-20260727-pmbd

つまずきポイント一覧

問題 原因 解決策
CloudFormationテンプレートのパースエラー pnpm stdout混入 テンプレート先頭の余計な行を削除
サブネットID不正 名前をIDと間違えた VPCコンソールからIDをコピー
ECSデプロイ失敗(Circuit Breaker) ECRクロスアカウントアクセス拒否 ターゲットアカウントリストに追加
DNS解決できない Route 53にレコード未作成 AレコードをALBエイリアスで作成
HTTP接続不可 ALBにHTTPリスナーなし HTTPSでアクセス
全ルート404 Secrets Manager設定不備 auth-configのissuerを正しく設定
ログイン時invalid_scope Cognitoスコープ設定ミス phoneを除外しprofileを追加
ログイン後「設定中」表示 アシスタント未有効化 管理画面(/admin/assistants)で有効化
/adminで404 ルートページなし /admin/を指定

まとめ

CDK synthテンプレートからのCloudFormationデプロイは、CDK bootstrapを省略できる反面、パラメータ入力やテンプレートの手動修正が必要です。

今回の経験で学んだポイント:

  • ECRクロスアカウントアクセスはリソースベースポリシーで制御されるため、デプロイ先アカウントの事前登録が必要
  • ECSのCircuit Breakerはイメージpull失敗時にも発動するため、まずECRアクセスを疑う
  • ヘルスチェック以外404のパターンは、アプリケーション起動時の設定読み込みエラーが原因の可能性がある
  • Cognitoのスコープ設定はデフォルト値に注意。アプリが要求するスコープと一致させる
  • アシスタントの有効化はインフラとは別のアプリケーション設定。DynamoDBで管理者権限を付与し、管理画面から操作する

特に、CloudFormationスタック自体は正常に作成されても、アプリケーションレベルの設定(Secrets Manager、Cognito、アシスタント有効化)が正しくないと動作しないケースは見落としがちです。インフラ構築とアプリケーション設定を分けて検証することをおすすめします。

この記事をシェアする

関連記事