ブラウザだけで使えるClaude Code環境をEC2 + code-server + Bedrockで作ってみた

ブラウザだけで使えるClaude Code環境をEC2 + code-server + Bedrockで作ってみた

EC2上のcode-serverにBedrock経由のClaude Codeを載せ、ブラウザだけで使える開発環境を作りました。認証はIAMロールなので利用者にAPIキーは不要で、研修やハンズオンで配る用途に向いています。
2026.08.13

こんにちは、林です。

EC2上にブラウザだけで使えるWeb IDE(code-server)を立てて、その中でBedrock経由のClaude Codeを使える環境を作ってみました。研修やハンズオンなど、一時的な開発環境を配りたいときに使えます。

Claude Codeの認証には、APIキーではなくEC2に付けたIAMロールを使います。利用者はブラウザでURLを開くだけで、認証設定なしにClaude Codeを使えます。

構築はAWSコンソールから進めますが、同じ構成のCloudFormationテンプレートも記事の最後に載せています。

code-server自体の構築手順は、こちらの記事を参考にさせていただきました。

https://dev.classmethod.jp/articles/ubuntu-2404-code-server-installation/

構成

今回作る環境の構成図です。

構成図

パブリックサブネットに置いたEC2でcode-serverを動かし、ブラウザから直接アクセスします。
Claude Codeも同じEC2上にインストールし、EC2に付けたIAMロールの権限でBedrockを呼び出します。

Bedrockのモデルアクセスを有効化する

Anthropicのモデルを初めて使うときは、ユースケースフォームの提出が必要です。
AWSコンソールで Amazon Bedrock(リージョン: 東京)を開き、Model catalog からAnthropicのClaudeモデルを選んで、ユースケースフォームを送信します。送信後はすぐに使えるようになります。

これは自分のIAMユーザーなどでコンソールから実施すればOKです。
EC2に付けるロールには、この操作(ユースケースフォームの提出)を実施する権限は必要ありません。

https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html

IAMロールを作成する

EC2に付与するIAMロールを用意します。
利用者が触るEC2に強い権限を載せるのは避けたいので、必要なBedrockのモデル呼び出しだけに絞ります。

信頼されたエンティティに EC2 を選んでロールを作成します(例: code-server-role)。
付与する権限は2つです。マネージドポリシーの AmazonSSMManagedInstanceCore と、下記のBedrockを呼び出す用のポリシーをインラインポリシーとして追加します。

AmazonSSMManagedInstanceCore はSession Manager経由でログインするために必要なもので、
これを付けておけばSSH用のポートを開けなくて済みます。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "BedrockInvokeAnthropicModels",
      "Effect": "Allow",
      "Action": [
        "bedrock:InvokeModel",
        "bedrock:InvokeModelWithResponseStream"
      ],
      "Resource": [
        "arn:aws:bedrock:*::foundation-model/anthropic.*",
        "arn:aws:bedrock:*:<アカウントID>:inference-profile/*.anthropic.*"
      ]
    }
  ]
}

<アカウントID> は自分のAWSアカウントID(12桁の数字)に置き換えてください。

このポリシーはAnthropic Claude系のモデル全般を許可しています。利用中にモデルを切り替える可能性がある場合はこの設定が楽です。

補足: 使うモデルを特定のものに絞りたい場合

コスト管理などの理由で、呼び出せるモデルを固定したいこともあります。
その場合は Resource を次のように差し替えます(例として jp.anthropic.claude-sonnet-4-6 に限定)。

[
  "arn:aws:bedrock:*:<アカウントID>:inference-profile/jp.anthropic.claude-sonnet-4-6",
  "arn:aws:bedrock:ap-northeast-1::foundation-model/anthropic.claude-sonnet-4-6",
  "arn:aws:bedrock:ap-northeast-3::foundation-model/anthropic.claude-sonnet-4-6"
]

1つのモデルに絞るのにARNが3つ必要なのは、クロスリージョン推論プロファイルを使っているためです。

1つ目はその推論プロファイル自体のARNで、Claude Codeが指定するモデルIDに対応します。
2つ目と3つ目は、推論プロファイルが処理を振り分ける先のfoundation-modelのARNです。
jp. プロファイルは東京・大阪にルーティングされるため、振り分け先となるこの2リージョン分も許可しないと呼び出せません。
あわせて、後述のユーザーデータで作成する ~/.claude/settings.jsonANTHROPIC_MODEL も、同じモデルIDに揃えてください。

IAMポリシーで許可したモデルと、Claude Codeが実際に呼び出すモデルが食い違うと AccessDeniedException になります。

VPCを作成する

code-serverにブラウザからアクセスするため、EC2はインターネットから到達できるパブリックサブネットに置きます。ここは今回の本題ではないので、VPCVPCを作成VPC など のウィザードでまとめて作ります。

項目 設定値
名前タグの自動生成 code-server(例)
IPv4 CIDR ブロック 10.0.0.0/16(デフォルトのまま)
アベイラビリティーゾーンの数 1
パブリックサブネットの数 1
プライベートサブネットの数 0
NATゲートウェイ なし
VPCエンドポイント なし

これでVPC・パブリックサブネット・インターネットゲートウェイ・ルートテーブルが一度に作成されます。
プライベートサブネットは今回使わないので作りません。NATゲートウェイも不要です(起動しているだけで課金されるため、作らないようにしてください)。

既存のVPCやデフォルトVPCを流用してもかまいません。その場合は、EC2を置くサブネットがパブリックサブネットであること(インターネットゲートウェイへのルートを持つこと)を確認してください。

EC2インスタンスを起動する

セキュリティグループを作成する

EC2を起動する前にセキュリティグループを用意します(例: code-server-sg)。
インバウンドは、code-serverが待ち受けるTCP 50443番を、自分のアクセス元IPからのみ許可します。

タイプ ポート範囲 ソース
カスタムTCP 50443 マイIP

ソースに マイIP を選ぶと、自分のグローバルIPが /32 のCIDRで自動入力されます。
code-serverの認証はパスワードのみなので、インターネット全体(0.0.0.0/0)に開けるのは避けましょう。

SSH用のTCP 22番は開けません。EC2へのログインには、Session Managerを利用します。

アウトバウンドはデフォルト設定(すべて許可)のままにします。code-serverとClaude Codeのインストール、Claude Codeの自動更新、Bedrock APIの呼び出しで、外向きの通信が必要になるためです。

インスタンスを起動する

用意したIAMロールとセキュリティグループを使って、EC2インスタンスを起動します。
設定するのは次の項目です。

項目 設定値
名前 code-server(例)
AMI Ubuntu Server 24.04 LTS
インスタンスタイプ t3.medium
キーペア キーペアなしで続行(Session Managerで入るため不要)
ネットワーク code-server-vpc とそのパブリックサブネット、
パブリックIPの自動割り当てを有効化
セキュリティグループ code-server-sg(既存のセキュリティグループを選択)
ストレージ 16GiB・gp3
IAMインスタンスプロファイル code-server-role高度な詳細内)
ユーザーデータ 下記のスクリプト(高度な詳細内)

code-server自体は軽量ですが、Claude Codeやエディタの拡張機能もあわせて動くことを考えると、t3.micro(メモリ1GiB)やt3.small(2GiB)ではメモリが不足しがちです。
そのため2vCPU・4GiBのt3.mediumにしています。より小さいサイズでの動作確認はしていません。

ストレージもデフォルトの8GiBから16GiBに増やしています。
OSとcode-server、Claude Codeで数GBを使うため、リポジトリのcloneやパッケージのインストールを見込んで、ある程度余裕を持たせた値です。インスタンスタイプとあわせて、用途に応じて調整してください。

ユーザーデータは 高度な詳細 を開いた先の、いちばん下にあります。

EC2起動ウィザードのユーザーデータ欄

ここに次のスクリプトを貼り付けます(Ubuntu 24.04の場合)。
code-serverとClaude Codeのインストール、Bedrock接続設定までを起動時に行います。

#!/bin/bash
set -eux

# Install code-server
apt update
apt install -y jq curl openssl

CODER_VERSION=$(curl -s https://api.github.com/repos/coder/code-server/releases/latest | jq -r .tag_name | sed 's/v//')
curl -fOL "https://github.com/coder/code-server/releases/download/v${CODER_VERSION}/code-server_${CODER_VERSION}_amd64.deb"
apt install -y ./code-server_${CODER_VERSION}_amd64.deb
rm -f code-server_${CODER_VERSION}_amd64.deb

mkdir -p /home/ubuntu/.config/code-server/
cat > /home/ubuntu/.config/code-server/config.yaml << EOF
bind-addr: 0.0.0.0:50443
auth: password
password: $(openssl rand -hex 16)
cert: true
EOF
chown -R ubuntu:ubuntu /home/ubuntu/.config
systemctl enable --now code-server@ubuntu

# Install Claude Code
sudo -u ubuntu bash -c 'curl -fsSL https://claude.ai/install.sh | bash'

mkdir -p /home/ubuntu/.claude
cat > /home/ubuntu/.claude/settings.json << 'EOF'
{
  "env": {
    "CLAUDE_CODE_USE_BEDROCK": "1",
    "AWS_REGION": "ap-northeast-1",
    "ANTHROPIC_MODEL": "jp.anthropic.claude-sonnet-4-6"
  }
}
EOF
chown -R ubuntu:ubuntu /home/ubuntu/.claude

# Add PATH
sudo -u ubuntu bash -c 'grep -q ".local/bin" /home/ubuntu/.bashrc || echo "export PATH=\"$HOME/.local/bin:\$PATH\"" >> /home/ubuntu/.bashrc'
Amazon Linux 2023の場合

AMIは Amazon Linux 2023 AMI を選択します。
ユーザーデータは次のスクリプトを使ってください。
違いは、パッケージ管理(apt→dnf)、既定ユーザー(ubuntu→ec2-user)、パッケージ形式(.deb→.rpm)です。

#!/bin/bash
set -eux

# Install code-server
dnf install -y jq openssl

CODER_VERSION=$(curl -s https://api.github.com/repos/coder/code-server/releases/latest | jq -r .tag_name | sed 's/v//')
curl -fOL "https://github.com/coder/code-server/releases/download/v${CODER_VERSION}/code-server-${CODER_VERSION}-amd64.rpm"
dnf install -y ./code-server-${CODER_VERSION}-amd64.rpm
rm -f code-server-${CODER_VERSION}-amd64.rpm

mkdir -p /home/ec2-user/.config/code-server/
cat > /home/ec2-user/.config/code-server/config.yaml << EOF
bind-addr: 0.0.0.0:50443
auth: password
password: $(openssl rand -hex 16)
cert: true
EOF
chown -R ec2-user:ec2-user /home/ec2-user/.config
systemctl enable --now code-server@ec2-user

# Install Claude Code
sudo -u ec2-user bash -c 'curl -fsSL https://claude.ai/install.sh | bash'

mkdir -p /home/ec2-user/.claude
cat > /home/ec2-user/.claude/settings.json << 'EOF'
{
  "env": {
    "CLAUDE_CODE_USE_BEDROCK": "1",
    "AWS_REGION": "ap-northeast-1",
    "ANTHROPIC_MODEL": "jp.anthropic.claude-sonnet-4-6"
  }
}
EOF
chown -R ec2-user:ec2-user /home/ec2-user/.claude

# Add PATH
sudo -u ec2-user bash -c 'grep -q ".local/bin" /home/ec2-user/.bashrc || echo "export PATH=\"$HOME/.local/bin:\$PATH\"" >> /home/ec2-user/.bashrc'

ユーザーデータの補足

code-serverは各OSの既定ユーザー動作するため、Claude Codeも sudo -u で同じユーザーにインストールしています。

Bedrockの接続設定は ~/.claude/settings.jsonenv に書いています。.bashrc に環境変数を書く方法もありますが、bashを経由しない起動では読み込まれないためです。

CLAUDE_CODE_USE_BEDROCK1 にすると、APIキーではなくAWSの認証情報(今回はインスタンスプロファイル)でBedrockを呼びます。AWS_REGION が呼び出し先リージョン、ANTHROPIC_MODEL が使うモデルの指定です。IAMポリシーはAnthropic系を広く許可しているので、モデルを変えたいときは ANTHROPIC_MODEL を書き換えてください。

ANTHROPIC_MODEL には、jp. のようなプレフィックスが付いたクロスリージョン推論プロファイルIDを指定してください。Claude Sonnet 4.6のような比較的新しいモデルは、プレフィックスなしの直接のモデルIDだとオンデマンド呼び出しに対応しておらず、on-demand throughput 非対応 のエラーになります。

動作確認

まず、ユーザーデータで自動生成されたcode-serverのパスワードを確認します。
EC2コンソールでインスタンスを選択し、接続Session Manager タブ → 接続でログインしてください。

# Ubuntuの場合
sudo cat /home/ubuntu/.config/code-server/config.yaml | grep password:

# Amazon Linux 2023の場合
sudo cat /home/ec2-user/.config/code-server/config.yaml | grep password:

ブラウザで https://<インスタンスのパブリックIPv4アドレス>:50443 にアクセスします(パブリックIPv4アドレスはインスタンスの詳細画面に表示されます)。自己署名証明書のため、次のような警告が出ます。

証明書の警告画面

「詳細設定」を押すと最下部に「<ホスト名> にアクセスする(安全ではありません)」というリンクが出るので、そこから進み、パスワードでログインしてください。

code-serverでターミナルを開き(メニューの TerminalNew Terminal)、claude コマンドを実行します。インスタンスプロファイル経由で認証情報が自動取得されるので、APIキーを設定しなくてもClaude Codeが立ち上がれば成功です。
実際にメッセージを送ってみて、指定したモデルから応答が返ってくればBedrock連携までOKです。

code-serverのターミナルで動作するClaude Code

起動時のヘッダーに Sonnet 4.6 · Amazon Bedrock と出ていれば、Bedrock経由で動いています。
認証まわりの状態だけを確認したい場合は claude auth status コマンドも使えます。

よくあるエラーと対処

手順どおりに進めれば基本的には発生しませんが、うまくいかないときの手がかりとして挙げておきます。

症状 原因と対処
claude: command not found PATHに ~/.local/bin が無い
ターミナルを開き直すか、PATHを通すコマンドを再実行する
AccessDeniedException IAMポリシーのResourceやアカウントIDを確認する、Bedrock呼び出し用ポリシーがロールに付いているかどうか、ユースケースフォームの提出も確認する
on-demand throughput エラー モデルIDが直接指定になっている
jp. などのプレフィックス付き推論プロファイルIDに変更する
リージョン系エラー AWS_REGION とプロファイルの地域(jp. / apac. / us.)が不一致
同じ地域にそろえる
ユーザーデータが動いていない Session Managerでログインし、
sudo cat /var/log/cloud-init-output.log でログを確認する
Session Managerでログインできない インスタンスが「実行中」か、
IAMロールに AmazonSSMManagedInstanceCore が付いているかを確認する

この構成では拡張機能をインストールできない

code-serverは、VS Codeと同じようにOpen VSX(拡張機能のマーケットプレイス)から拡張機能を入れられます。
ところが今回の構成では、次のようなエラーでインストールに失敗します。これはClaude Code用の拡張に限った話ではなく、Open VSXから入れる拡張機能全般に起きます。

IDE: ✘ Error installing VS Code extension: Command failed with ERR_STREAM_PREMATURE_CLOSE

これはcode-serverを自己署名証明書のHTTPSで動かしている影響とみています。

Claude Code自身にも、ターミナルで使うCLI版のほかに、サイドパネルとして組み込める拡張機能版があります。ファイルを見ながら会話したり、変更箇所をエディタ上で確認したりできるものですが、この環境では同じ理由でインストールできません。

拡張機能(Claude Code用に限らず)も使いたい場合は、CloudFrontを前段に置くなどして、ブラウザに信頼される証明書でアクセスできるようにする必要があります。


CloudFormationで自動構築する

ここまでの構成(VPC・セキュリティグループ・IAMロール・EC2)を一度に作成できるCloudFormationテンプレートも用意しました。OSごとに分けています。

Ubuntu 24.04版 テンプレート全文
code-server-ubuntu.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: code-server + Claude Code (Amazon Bedrock) on Ubuntu 24.04
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Set basic information.
        Parameters:
          - SystemName
      - Label:
          default: Set your EC2 instance settings.
        Parameters:
          - InstanceType
          - AllowedIP
          - AMIID
Parameters:
  SystemName:
    Description: "Prefix of each resource name."
    Type: String
    Default: "code-server"
  InstanceType:
    Description: "EC2 instance type."
    Type: String
    Default: "t3.medium"
  AllowedIP:
    Description: "Your access source IP in CIDR notation (e.g. 203.0.113.10/32)."
    Type: String
  AMIID:
    Description: "Ubuntu 24.04 AMI ID."
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/canonical/ubuntu/server/24.04/stable/current/amd64/hvm/ebs-gp3/ami-id
Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-vpc"
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-igw"
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway
  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [0, !GetAZs ""]
      VpcId: !Ref VPC
      CidrBlock: 10.0.1.0/24
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-public-subnet"
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-public-rtb"
  PublicRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
  PublicRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet

  CodeServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub "${SystemName}-sg"
      GroupDescription: "Security group for code-server (HTTPS on 50443)"
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 50443
          ToPort: 50443
          CidrIp: !Ref AllowedIP
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-sg"

  CodeServerRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub
        - "${SystemName}-role-${Suffix}"
        - Suffix: !Select [4, !Split ["-", !Select [2, !Split ["/", !Ref "AWS::StackId"]]]]
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - "ec2.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
      Policies:
        - PolicyName: "BedrockInvokeAnthropicModels"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Sid: "BedrockInvokeAnthropicModels"
                Effect: "Allow"
                Action:
                  - "bedrock:InvokeModel"
                  - "bedrock:InvokeModelWithResponseStream"
                Resource:
                  - "arn:aws:bedrock:*::foundation-model/anthropic.*"
                  - !Sub "arn:aws:bedrock:*:${AWS::AccountId}:inference-profile/*.anthropic.*"
  CodeServerInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref CodeServerRole

  CodeServerEC2:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref AMIID
      InstanceType: !Ref InstanceType
      BlockDeviceMappings:
        - DeviceName: /dev/sda1
          Ebs:
            Encrypted: true
            VolumeSize: 16
            VolumeType: gp3
            DeleteOnTermination: true
      NetworkInterfaces:
        - AssociatePublicIpAddress: true
          DeviceIndex: "0"
          SubnetId: !Ref PublicSubnet
          GroupSet:
            - !Ref CodeServerSecurityGroup
      IamInstanceProfile: !Ref CodeServerInstanceProfile
      Tags:
        - Key: Name
          Value: !Ref SystemName
      UserData:
        Fn::Base64: |
          #!/bin/bash
          set -eux

          # Install code-server
          apt update
          apt install -y jq curl openssl

          CODER_VERSION=$(curl -s https://api.github.com/repos/coder/code-server/releases/latest | jq -r .tag_name | sed 's/v//')
          curl -fOL "https://github.com/coder/code-server/releases/download/v${CODER_VERSION}/code-server_${CODER_VERSION}_amd64.deb"
          apt install -y ./code-server_${CODER_VERSION}_amd64.deb
          rm -f code-server_${CODER_VERSION}_amd64.deb

          mkdir -p /home/ubuntu/.config/code-server/
          cat > /home/ubuntu/.config/code-server/config.yaml << EOF
          bind-addr: 0.0.0.0:50443
          auth: password
          password: $(openssl rand -hex 16)
          cert: true
          EOF
          chown -R ubuntu:ubuntu /home/ubuntu/.config
          systemctl enable --now code-server@ubuntu

          # Install Claude Code
          sudo -u ubuntu bash -c 'curl -fsSL https://claude.ai/install.sh | bash'

          mkdir -p /home/ubuntu/.claude
          cat > /home/ubuntu/.claude/settings.json << 'EOF'
          {
            "env": {
              "CLAUDE_CODE_USE_BEDROCK": "1",
              "AWS_REGION": "ap-northeast-1",
              "ANTHROPIC_MODEL": "jp.anthropic.claude-sonnet-4-6"
            }
          }
          EOF
          chown -R ubuntu:ubuntu /home/ubuntu/.claude

          # Add PATH
          sudo -u ubuntu bash -c 'grep -q ".local/bin" /home/ubuntu/.bashrc || echo "export PATH=\"$HOME/.local/bin:\$PATH\"" >> /home/ubuntu/.bashrc'

Outputs:
  PublicIP:
    Description: "code-server public IP. Access it at https://<PublicIP>:50443"
    Value: !GetAtt CodeServerEC2.PublicIp
Amazon Linux 2023版 テンプレート全文
code-server-al2023.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: code-server + Claude Code (Amazon Bedrock) on Amazon Linux 2023
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Set basic information.
        Parameters:
          - SystemName
      - Label:
          default: Set your EC2 instance settings.
        Parameters:
          - InstanceType
          - AllowedIP
          - AMIID
Parameters:
  SystemName:
    Description: "Prefix of each resource name."
    Type: String
    Default: "code-server"
  InstanceType:
    Description: "EC2 instance type."
    Type: String
    Default: "t3.medium"
  AllowedIP:
    Description: "Your access source IP in CIDR notation (e.g. 203.0.113.10/32)."
    Type: String
  AMIID:
    Description: "Amazon Linux 2023 AMI ID."
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64
Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-vpc"
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-igw"
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway
  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [0, !GetAZs ""]
      VpcId: !Ref VPC
      CidrBlock: 10.0.1.0/24
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-public-subnet"
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-public-rtb"
  PublicRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
  PublicRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet

  CodeServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub "${SystemName}-sg"
      GroupDescription: "Security group for code-server (HTTPS on 50443)"
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 50443
          ToPort: 50443
          CidrIp: !Ref AllowedIP
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-sg"

  CodeServerRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub
        - "${SystemName}-role-${Suffix}"
        - Suffix: !Select [4, !Split ["-", !Select [2, !Split ["/", !Ref "AWS::StackId"]]]]
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - "ec2.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
      Policies:
        - PolicyName: "BedrockInvokeAnthropicModels"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Sid: "BedrockInvokeAnthropicModels"
                Effect: "Allow"
                Action:
                  - "bedrock:InvokeModel"
                  - "bedrock:InvokeModelWithResponseStream"
                Resource:
                  - "arn:aws:bedrock:*::foundation-model/anthropic.*"
                  - !Sub "arn:aws:bedrock:*:${AWS::AccountId}:inference-profile/*.anthropic.*"
  CodeServerInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref CodeServerRole

  CodeServerEC2:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref AMIID
      InstanceType: !Ref InstanceType
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            Encrypted: true
            VolumeSize: 16
            VolumeType: gp3
            DeleteOnTermination: true
      NetworkInterfaces:
        - AssociatePublicIpAddress: true
          DeviceIndex: "0"
          SubnetId: !Ref PublicSubnet
          GroupSet:
            - !Ref CodeServerSecurityGroup
      IamInstanceProfile: !Ref CodeServerInstanceProfile
      Tags:
        - Key: Name
          Value: !Ref SystemName
      UserData:
        Fn::Base64: |
          #!/bin/bash
          set -eux

          # Install code-server
          dnf install -y jq openssl

          CODER_VERSION=$(curl -s https://api.github.com/repos/coder/code-server/releases/latest | jq -r .tag_name | sed 's/v//')
          curl -fOL "https://github.com/coder/code-server/releases/download/v${CODER_VERSION}/code-server-${CODER_VERSION}-amd64.rpm"
          dnf install -y ./code-server-${CODER_VERSION}-amd64.rpm
          rm -f code-server-${CODER_VERSION}-amd64.rpm

          mkdir -p /home/ec2-user/.config/code-server/
          cat > /home/ec2-user/.config/code-server/config.yaml << EOF
          bind-addr: 0.0.0.0:50443
          auth: password
          password: $(openssl rand -hex 16)
          cert: true
          EOF
          chown -R ec2-user:ec2-user /home/ec2-user/.config
          systemctl enable --now code-server@ec2-user

          # Install Claude Code
          sudo -u ec2-user bash -c 'curl -fsSL https://claude.ai/install.sh | bash'

          mkdir -p /home/ec2-user/.claude
          cat > /home/ec2-user/.claude/settings.json << 'EOF'
          {
            "env": {
              "CLAUDE_CODE_USE_BEDROCK": "1",
              "AWS_REGION": "ap-northeast-1",
              "ANTHROPIC_MODEL": "jp.anthropic.claude-sonnet-4-6"
            }
          }
          EOF
          chown -R ec2-user:ec2-user /home/ec2-user/.claude

          # Add PATH
          sudo -u ec2-user bash -c 'grep -q ".local/bin" /home/ec2-user/.bashrc || echo "export PATH=\"$HOME/.local/bin:\$PATH\"" >> /home/ec2-user/.bashrc'

Outputs:
  PublicIP:
    Description: "code-server public IP. Access it at https://<PublicIP>:50443"
    Value: !GetAtt CodeServerEC2.PublicIp

AllowedIP に自分のアクセス元IPを指定してデプロイします。

aws cloudformation create-stack --stack-name temp-code-server \
    --template-body file://./code-server-ubuntu.yaml \
    --parameters "ParameterKey=AllowedIP,ParameterValue=<自分のアクセス元IP>/32" \
    --capabilities CAPABILITY_NAMED_IAM

Amazon Linux 2023版は --template-bodycode-server-al2023.yaml に変えてください。
動作確認はコンソールで作った場合と同じです。

使うモデルを変えたい場合は、テンプレートのユーザーデータ内、~/.claude/settings.jsonANTHROPIC_MODEL を書き換えてください。


さいごに

EC2上のcode-serverにBedrock経由のClaude Codeを載せた環境を、AWSコンソールから構築しました。
認証はEC2に付けたIAMロールで行うので、利用者にAPIキーを配る必要がありません。ロールに与える権限も、Bedrockのモデル呼び出しだけに絞れます。

手元の端末にClaude Codeをインストールできない場合や、まずは手軽に触ってみたい場合に試してみてください。

この記事がどなたかの参考になれば幸いです。最後までご覧いただきありがとうございました!

参考

https://dev.classmethod.jp/articles/ubuntu-2404-code-server-installation/

https://dev.classmethod.jp/articles/use-code-server-as-temporary-cloud9-alternative/

https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html

https://docs.anthropic.com/en/docs/claude-code/amazon-bedrock


Claudeならクラスメソッドにお任せください

クラスメソッドは、Anthropic社とリセラー契約を締結しています。各種製品ガイドから、業種別の活用法、フェーズごとのお悩み解決などサービス支援ページにまとめております。まずはご覧いただき、お気軽にご相談ください。

サービス詳細を見る

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事