🤖

コーディングエージェントを自由に暴れさせるためのサンドボックス環境

に公開

はじめに

こんにちは!エクサウィザーズのアドベントカレンダー20日目を担当させていただきました、4月から新卒として入ったエクサウィザーズの綱島です。

今回は、昨今話題のコーディングエージェント(e.g, Claude Code、Codex CLI、Cursor、Gemini CLI)についてです。
コーディングエージェントは、当社でも導入しており、我々の作業を確実に加速させてくれています。
特に、README作成や叩き台の作成、モック作成、ログ管理の記述などでは大変重宝する存在で、おそらく私はコーディングエージェント無しでの開発はもうめんどくさくてできない気がします。

さて、話が逸れてしまいましたが、コーディングエージェントは便利な反面、強い権限を与えて完全に自由に動かすと、とんでもないことをしでかす可能性があります(e.g., システムファイルの削除、重要データの削除)。
本記事ではコーディングエージェントを「安全に」「自由に」動かすための、AWS EC2インスタンスを使ったサンドボックス環境作成の紹介になります。EC2インスタンスに付する権限を絞れば物理的なガードレールになります。
一応、他の選択肢としてはローカルでDevコンテナを使うという手が一番手軽ではありますが、完全分離という観点から今回はEC2インスタンスを使った記事になります。

この記事で世界で一人でもコーディングエージェントに気を狂わされる人が減ることを願っています。

用語整理

本題に入る前に、この記事で出てくる用語を整理しておきます。

コーディングエージェント

ざっくり言うとLLMがコードを書いてくれるツールです。

ChatGPTみたいにチャットでコードを教えてもらうのとは違い、エージェント自身がファイルを読み書きしたり、ターミナルでコマンドを実行したりできます。つまり、人間の代わりに実際の開発作業をやってくれるやつです。

余談にはなりますが、エージェントという言葉が昨今あまりにも適当な文脈で使われすぎているので、若干モヤモヤしているところではあります。
私的には、環境からフィードバックを受け取り、行動を取って環境とインタラクションするものがエージェントという印象ではあります。
そういう意味ではコーディングエージェントはVSCodeなどの環境とインタラクションをするという意味では結構しっくり来るところではあります。

ただ、転職エージェントやsshエージェントなど多様な意味でエージェントという言葉は分野ごとに使われてはいたので、しょうがないかなぁという気もしています(一時期の全てのものをAIと呼ぶようになっていてモヤモヤしていたときの記憶が蘇ってきます)。

サンドボックス環境

サンドボックス環境は壊しても大丈夫な隔離された環境のことです。

砂場(sandbox)で子供が自由に遊んでも周りが汚れないように、サンドボックス環境では何をやらかしても大丈夫ということに由来があります。

AWS、EC2

AWSはAmazonのクラウドサービスです。Elastic Compute Cloud (EC2) はAWSの中の仮想サーバーを借りられるサービスです。
使いたいときに立ち上げて、いらなくなったら消せる、壊れたら作り直せばいいというものです。

ただ、EC2を初めて使う時は設定が多すぎて何が何やらというふうにもなりますし、インスタンスの停止と削除がわかりにくい(今はだいぶ改善しましたが)という問題もあります。
なので、本記事では必要最小限の話のみをしてなるべく混乱しないような進行を心がけて書いていきます。

IAMロール

Identity and Access Management (IAM) ロールはAWSリソースに付与する権限の依代です。
IAMというAWSのサービスの中の1機能です。

人間のユーザーに付与する権限とは別で、AWSのサービス同士がやり取りするときに使う通行証のようなものだと思ってもらえればいいです。
今回はEC2インスタンスにアクセスするために使います。

Terraform

HashiCorpというところが作成管理しているインフラをコードで管理するツールです。
通称IaC(Infrastructure as Code)のツールの1つです。
記述はHashiCorp Configuration Language (HCL) というIaC専用の言語を用いて、Terraformでインフラの作成管理などを行うことができます。

同じ環境をもう一回作りたい、ちょっと設定変えたいってときに便利です。
手作業だと「あれ、どこの設定いじったんだっけ、、、」ってなりがちですが、Terraformならコードを見れば全部わかります。

本記事では最初に手軽で簡単なAWSコンソール(WebのGUIの設定画面)からの設定方法を紹介し、その後にTerraformでの設定方法を紹介します。

EC2インスタンスで自由に破壊してもらって大丈夫な環境を作ろう!

AWSコンソール編

さて、本題に入って行きましょう。

こちらはAWSアカウントを事前に作ってあるという前提で話をしていきますので、AWSアカウント作成自体についてはAWSアカウント作成の流れを参照してください。
ちなみにクレカさえあれば、低額で始められるので、気軽にやってみてもらえると思います(今回使うEC2インスタンスとストレージ合わせて1時間あたり0.0584ドル、日本円で約9円ほどです)。
AWSアカウント自体は従量課金コンテンツを保持していなければ料金はかかりません。

順序としては以下の通りです。

  1. IAMロールを作成
  2. EC2インスタンスの起動
  3. EC2インスタンス内にアクセスする
  4. EC2インスタンスにコーディングエージェントをインストールする
  5. コーディングエージェントに暴れてもらう

1. IAMロールを作成

まずは、EC2インスタンスを立ち上げる前にIAMロールを作っておく必要があります。
こちらを先に作っておかないと後でEC2インスタンスを作るときに行ったり来たりして面倒になるからです。

さて、アカウントの中に入って左上の検索欄で"iam"と入力すると以下のようになると思いますので、IAMというサービスをクリックするか、Enterを押すとIAMのサービスに飛べます。

iam_0

IAMに飛ぶと、以下のような画面になっており、左側の赤丸のロールをクリックし、右上のロールを作成を起動をクリックします。
iam_1

ロール作成画面になると、まずは信頼されたエンティティタイプとユースケースというところに出くわすと思います。
ここではAWSのサービス、EC2をそれぞれ選んで、右下の次へを押してください。

iam_2

続いて、許可を追加という項目において、許可ポリシーというところだけを設定します。これは、権限の依代であるIAMロールに、権限を付与する工程にあたります。
許可ポリシーの検索欄に"ssmmanaged"と入力すると下図のように2つだけポリシーが出てきますので、AmazonSSMManagedInstanceCoreを選択して、右下の次へをクリックします。
"ssm"とだけ調べるとたくさんのSSM関連のポリシーが出てきますが、FullAccessだと過剰な権限、AmazonSSMManagedInstanceCore以外だと権限的に満たしていないです。
なぜこのようなややこしいことをしているかなのですが、基本的にAWSでは最小権限の原則を満たすことが推奨されており、過剰な権限は何かしらの事故に繋がるという考えからきています。

iam_3

最後に名前、確認、および作成というところにきますが、ロール名と書かれているところだけ埋めればよいです。"coding-agent_sandbox_role"とでもしておきましょう。
さぁ、あとは右下のロールを作成をクリックして完了しましょう。

iam_4

2. EC2インスタンスの起動

アカウントの中に入って左上の検索欄で"ec2"と入力すると以下のようになると思いますので、EC2というサービスをクリックするか、Enterを押すとEC2のサービスに飛べます。
console_1

EC2に飛ぶと、以下のような画面になっており、左側の赤丸のインスタンスをクリックし、右上のインスタンスを起動をクリックします。
console_2

ここで大量の設定が出てきて圧倒されると思いますが、今回設定するところは以下の7項目です(7つだとしても多いですね。AWSに初めて触って躓くポイントとして設定項目が多すぎて意味不明なことだと思います。しょうがないとは思う部分も多いですが、やはり最初にこれを見せられると私はやる気を失いました)。

  • 名前とタグ
  • アプリケーションおよび OS イメージ (Amazon マシンイメージ)
  • インスタンスタイプ
  • キーペア
  • ネットワーク設定
  • ストレージ設定
  • IAMインスタンスプロフィール

console_3

名前とタグ

ここはなんでもいいです。なんでもいいというのは本当になんでもいいです。
適当に今回はcoding-agent_sandboxとかにしておきましょう。

ただし、チームや組織で運用している場合は、作った人がわかるようにするという意図で名前を入れたりしましょう。
例えば、私の場合はhideki.tsunashima_coding-agent_sandboxとかです。

アプリケーションおよび OS イメージ (Amazon マシンイメージ)

ここは単純にOSを選んでもらうと思っていただいていいです(AMIなどは今回は関係ないので、無視でいいです)。
今回はおそらくこの記事を読んでいただいている皆さんなら馴染み深いであろうUbuntuにしていこうと思います。
バージョンは24.04 LTSを選択しました(2025年12月15日現在はこちらがUbuntuをクリックした時のデフォルトになっています)。

インスタンスタイプ

インスタンスタイプはマシンスペックだと思ってもらって構いません。
こちらもあまりに大量の選択肢が出てきて眩暈がすると思いますが、今回はt3.mediumにしていきましょう。

AWSのインスタンスはxx.yyという形式になっていまして、xxがCPUかGPUのタイプ別になっており、yyがsmallやlargeなどの表記でメモリやvCPU数になっています。
ちなみにt3は一般用途向け(汎用)と呼ばれており、CPUやメモリのバランスがいいインスタンスになっています。
GPUを使ってガリガリ機械学習をしていきたい人は、高速コンピューティングインスタンスのPシリーズ等を使っていくことになります(P6とかでは最新のB200が載ったノードを利用することができます)。

また、t3はyyの部分が7種類あり、中間に位置するmediumを今回は利用しています(コーディングエージェントを動かすにあたって、ある程度メモリを確保しておこうという意図があります)。金額的にはt3.mediumで1時間あたり0.0544ドルと1日回しても1.3ドル程度で、200円程度です。主に料金として乗っかってくるところの1つはここで、もう一つはストレージです。

インスタンスタイプについて知りたい人はAmazon EC2 インスタンスタイプ、金額を知りたい人はAmazon EC2 オンデマンド料金を見ると良いと思います。

キーペア

EC2インスタンスでは、インスタンス内にアクセスするにあたってキーペアという、公開鍵認証におけるAWS専用の公開鍵と秘密鍵のペアのようなものを用意するのが1つの選択肢となっています。

ただ、今回は安全にアクセスしつつ、必要最低限の権限に抑えるという説明の観点からSystems Manager (SSM) というAWSのサービスを用いた認証方式を採用します。
なので、トグルをクリックして「キーペアなしで続行(推奨されません)」を選択します(推奨されないのは、SSMを使わないとアクセスできなくなるからです)。
ちなみになんでSystems ManagerなのにSSMなのかというと、昔はEC2 Simple Systems Managerという名前だったので、その名残でSSMとなっています。

ネットワーク設定

ここも初めて見たら意味不明ですよね。
簡単にいうと、AWS上でのローカルネットワークの構築みたいなことをやっています。
EC2インスタンスは、このローカルネットワークのVirtual Private Cloud (VPC) の中でしか起動できないので、勝手にデフォルトの値が選択されています。
これはこのままでいいです。

ただし、「からのSSHトラフィックを許可」と書いてあるところのチェックマークは外しておきます。
これをチェックしたままにしていると、キーペアさえあれば任意のSSHトラフィック接続が可能になってしまいます。
「え、キーペアないとアクセスできないなら別にそんなことしなくてもいいんじゃ?」と私も最初は思っていました。
ただ、セキュリティでは多層防御(Defense in Depth)という考えがあり、簡単にいうと念の為色々なパターンで守っておこうというものです。
今回では「キーペア自体を設定しない」、「キーペアがあったとしてもアクセスできない」、「特定のIAMロールがないとアクセスできない」という三層防御です。
となると再度、「キーペア自体を設定しない」、「キーペアがあったとしてもアクセスできない」の2つは流石に無駄じゃ、、、となりますが、万が一どちらか設定を間違えた時のための予防策です。

ストレージ設定

ここは単純にストレージを設定するのですが、今回のユースケースを試すだけでは別に設定は不要です。

ただ、今後コーディングエージェントが生成したスクリプト等が肥大化したときに備えて一応試しに30GiBくらいにしておきましょう。
gp3と書いてあるところはそのままでいいです。gp2よりgp3の方が高速、とSSDの性能くらいに思っておいて問題ないです。

IAMインスタンスプロフィール

ここだけは高度な詳細と書かれているところから見る必要があります。
高度な詳細のトグルを開いて、上から2番目のIAMインスタンスプロフィールのトグルをクリックし、先ほど作成した"coding-agent_sandbox_role"を選択してあげましょう。

さてさて、やっとここまできました。
あとは右下のインスタンスの起動をクリックするだけです。起動に成功したら以下のような画面が出るはずです。

console_4

3. EC2インスタンス内にアクセスする

では、またインスタンスの画面に戻って先ほど起動したインスタンスを確認してみましょう。
以下の画像のようにステータスチェックが「3/3のチェックに合格しました」となっていれば、準備は完了です。

console_6

その上で、画像上部にある赤丸の接続をクリックし、以下の画面でセッションマネージャータブをクリックしましょう。
あとは、右下の接続をクリックするだけです。
無事アクセスできたら怪しい真っ黒な画面になっているはずです。

console_7

4. EC2インスタンスにコーディングエージェントをインストールする

SSMでアクセスすると、bashが起動していないので、bashを起動してホームディレクトリに移動しましょう。

bash
cd ~

その後にClaude Codeのインストールページを参考に、以下のコマンドを入力します。

curl -fsSL https://claude.ai/install.sh | bash

無事インストールされたら、Claudeとターミナルでタイプして、あとはClaude Codeの初期設定手順に従うだけです!
全てが完了したら以下のような画面になっているはずです。

ssm_1

5. コーディングエージェントに暴れてもらう

さて、もう考えることはありません。
Ctrl+Cを2回押して、一度外に出て、claude --dangerously-skip-permissionsとタイプして全権限を渡してしまいましょう。
そうすると、本当にええんか?と聞かれるので、Yes, I acceptを選択することで、Bypass Permissions modeになります。

ssm_2

ここまでにおいて、EC2インスタンスの作成によってシステムファイルが削除されようがまた作り直せば良いという状況を作りました。
また、EC2インスタンス自体にSSMの権限以外を与えていないことで、意図せずClaude CodeがAWSアカウント内の他のリソースを破壊するということもなくなりました。
あとは思う存分暴れてもらいましょう!※

※ ただし、インターネット上の悪意のあるコード実行等の危険性は避けられないので、意図せず加害者にならないか等には十分気をつけてください。これはサンドボックス環境を作ろうが作らなかろうが起こりうる問題です。

番外編. EC2インスタンスを削除しよう

起動して満足してはいけません。
EC2インスタンスは削除し忘れると永遠に料金が加算されます。一緒に削除していきましょう。
以下の画像のようにインスタンスを選択した状態でインスタンスの状態トグルから、赤丸のインスタンスを終了(削除)をクリックしましょう。
クリックするといろいろ言われますが、そのまま右下の終了(削除)をクリックしてEC2インスタンスを削除しちゃいましょう。

console_8

昔は、終了のみしか書いておらず、停止なのかなんなのかがわかりづらいというトラップがあったようですが、流石に不評すぎて(削除)の文言が追加されたのでしょうか。
ちなみに、停止を選択すると、インスタンスの料金はそこでかからなくなりますが、ストレージに対する課金は続きます。

Terraform編

さてさて、せっかくなので、コンソールで触るだけでなく、Terraformでこれまでのリソース作成をやっちゃいましょう!
以下は再掲になりますが、主に1, 2の部分をコードとして記述することで、容易に削除もできる上に管理もしやすくなります。
また、ついでなので、3, 4, 5についてはVSCodeを使ったやり方をやってみましょう。

  1. IAMロールを作成
  2. EC2インスタンスの起動
  3. EC2インスタンス内にアクセスする
  4. EC2インスタンスにコーディングエージェントをインストールする
  5. コーディングエージェントに暴れてもらう

1. IAMロールを作成 & 2. EC2インスタンスの起動

Terraformのインストール

まずは、大前提となるTerraformをインストールしていきましょう。
こちらのHashiCorpのインストールページからインストールして、以下のコマンドで正しく入っているか確かめましょう。

terraform -help

Terraformのファイル作成

それでは、インストールができたので、Terraformで管理する3つのファイルを作っていきます。
1つ目は、プロバイダーを定義するファイルです。
TerraformはAWSに限らず様々なプロバイダーのインフラを記述できるので、まずはどこのインフラかを宣言する必要があります。ちなみにTerraformでは#でコメントアウトができます。
参考:Terraform公式ドキュメントのawsのproviderについて

provider.tf
# ========================================
# terraformセクション
# ========================================
# ここではTerraform自体の設定を行います。
# required_providers: 使用するプロバイダー(今回はAWS)を指定します。
#                     source: プロバイダーの取得元(hashicorp/awsはHashiCorp公式のAWSプロバイダー)
#                     version: プロバイダーのバージョン(“~> 6.0"は6.x系を使うという意味)
terraform {
  required_providers {
    aws = {
      source  = “hashicorp/aws"
      version = “~> 6.0"
    }
  }
}
# ========================================
# providerセクション
# ========================================
# ここではAWSプロバイダーの設定を行います。
# TerraformはAWSだけでなく、GCPやAzureなど様々なクラウドに対応しています。
# そのため、「どのクラウドのどのリージョンを使うか」を指定する必要があります。
# profile: 後ほど使うaws loginコマンドは2025年11月の機能で,まだ公式が対応していないため(たぶん),このような形でcredentialを取得
# region: AWSのリージョン(データセンターの場所)を指定します。
#         ap-northeast-1は東京リージョンです。
provider “aws" {
  profile = “terraform-process"
  region  = “ap-northeast-1"
}

2つ目はIAMロールのファイルです。
IAMロール(権限の依代)の作成、IAMロールにSSMの権限を付与、EC2用のインスタンスプロファイルとしてIAMロールを登録の3つの手順になります。

iam.tf
# ========================================
# IAMロールの作成
# ========================================
# EC2インスタンスがAWSサービスを利用するための基本的なIAMロールで権限の依代
resource “aws_iam_role" “coding_agent_sandbox_role" {
  # IAMロールの名前(AWSアカウント内で一意である必要がある)
  name = “coding-agent_sandbox_role"
  # 信頼ポリシー(Trust Policy / AssumeRole Policy)
  # このポリシーは「どのAWSサービスやエンティティがこのロールを引き受けられるか」を定義
  # EC2サービスがこのロールを引き受けることを許可している
  assume_role_policy = jsonencode({
    # IAMポリシー言語のバージョン(基本的に2012-10-17を使用)
    Version = “2012-10-17"
    Statement = [
      {
        # sts:AssumeRole => Security Token Serviceの一時的な認証情報を取得するアクション.EC2インスタンスがこのロールの権限を使用できるようにする
        # Allow => このアクションを許可する(Denyも指定可能)
        # Principal => 誰がこのアクションを実行できるか.ここではEC2サービス(ec2.amazonaws.com)を指定
        Action = “sts:AssumeRole"
        Effect = “Allow"
        Principal = {
          Service = “ec2.amazonaws.com"
        }
      }
    ]
  })
}
# ========================================
# マネージドポリシーのアタッチ
# ========================================
# AWSが提供する事前定義済みのマネージドポリシーをロールにアタッチ
resource “aws_iam_role_policy_attachment" “ssm_managed_instance_core" {
  # アタッチ先のIAMロール名を指定
  role       = aws_iam_role.coding_agent_sandbox_role.name
  # AmazonSSMManagedInstanceCore = Systems Manager (SSM)の基本機能を使用するための権限
  # このポリシーにより、EC2インスタンスに対してSSM経由でのリモート接続やコマンド実行が可能になる
  # SSHキーやパスワード不要でセキュアにインスタンスを管理できる
  #
  # ARN (Amazon Resource Name) の構造:
  #   arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
  #   ^^^─┬─^^─┬─^^─┬─^^─┬─^^──────────┬──────────────────^
  #       │    │    │    │            │
  #       │    │    │    │            └─ リソース名(ポリシー名)
  #       │    │    │    └─ アカウントID(“aws"はAWS管理のリソースを示す)
  #       │    │    └─ リージョン(IAMはグローバルサービスなので空欄)
  #       │    └─ サービス名(iam = Identity and Access Management)
  #       └─ パーティション(通常は“aws"、中国リージョンなら“aws-cn")
  #
  # ARNの種類:
  #   - AWS管理ポリシー: arn:aws:iam::aws:policy/ポリシー名
  #   - カスタマー管理ポリシー: arn:aws:iam::123456789012:policy/ポリシー名
  #                             (123456789012は自分のAWSアカウントID)
  policy_arn = “arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
# ========================================
# インスタンスプロファイルの作成
# ========================================
# EC2インスタンスプロファイルは、IAMロールをEC2インスタンスに関連付けるためのコンテナ
# EC2インスタンスはIAMロールを直接使用できないため、インスタンスプロファイルを経由する必要がある
#
# 仕組み:
#   1. EC2インスタンス起動時にインスタンスプロファイルを指定
#   2. インスタンスプロファイルが関連付けられたIAMロールの権限をインスタンスに付与
#   3. インスタンス内のアプリケーションがAWS APIを呼び出す際、自動的にロールの一時認証情報を使用
resource “aws_iam_instance_profile" “coding_agent_sandbox_profile" {
  name = “coding-agent_sandbox_profile"
  # このインスタンスプロファイルに関連付けるIAMロール
  # EC2インスタンスはこのロールの権限を継承する
  role = aws_iam_role.coding_agent_sandbox_role.name
}

最後はEC2インスタンスのファイルです。
OSの決定(Amazon Machine Image (AMI) 選定)、SSHのインバウンドを拒否するセキュリティグループの作成、EC2インスタンスの作成の3つになります。
AMIやセキュリティグループは初登場の概念ですが、今回はそこまで気にしなくてもいいので割愛します。

ec2.tf
# ========================================
# データソース: AMI(Amazon Machine Image)の取得
# ========================================
# データソースは既存のAWSリソースの情報を取得するために使用
# リソース(resource)が新しく作成するのに対し、データソース(data)は既存の情報を参照するのみ
data “aws_ami" “ubuntu" {
  # 最新のAMIを取得する(複数マッチした場合、作成日時が最新のものを選択)
  most_recent = true
  # AMIの所有者を指定(099720109477 = Canonical社のAWSアカウントID)
  # 公式のUbuntu AMIのみを検索対象にすることで、信頼できないイメージの使用を防ぐ
  owners = [“099720109477"]
  # フィルター1: AMI名のパターンマッチング
  filter {
    name = “name"
    # ubuntu-noble = Ubuntu 24.04のコードネーム
    # hvm-ssd-gp3 = HVM仮想化、SSD (gp3)ストレージ
    # amd64 = x86_64アーキテクチャ
    # server = サーバー版(デスクトップ版ではない)
    # * = ワイルドカード(バージョン番号などが変わる部分)
    values = [“ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*“]
  }
  # フィルター2: 仮想化タイプの指定
  filter {
    name = “virtualization-type"
    # HVM (Hardware Virtual Machine) = 完全仮想化
    # 準仮想化(PV)より高性能で、現在のEC2インスタンスタイプでは必須
    values = [“hvm"]
  }
}
# ========================================
# セキュリティグループ(ファイアウォール)
# ========================================
# セキュリティグループはEC2インスタンスのファイアウォールとして機能
# インバウンド(入ってくる)とアウトバウンド(出ていく)トラフィックを制御
resource “aws_security_group" “coding_agent_sandbox_sg" {
  name        = “coding-agent_sandbox_sg"
  description = “Security group for coding agent sandbox - no SSH access"
  # アウトバウンドルール(Egress): インスタンスから外部への通信
  # インターネットアクセス、パッケージのダウンロード、API呼び出しなどに必要
  egress {
    # ポート範囲: 0(すべてのポート)
    from_port = 0
    to_port   = 0
    # プロトコル: -1 = すべてのプロトコル(TCP、UDP、ICMPなど)
    protocol = “-1"
    # CIDR表記: 通信を許可する送信先IPアドレスの範囲
    # 0.0.0.0/0 = すべてのIPv4アドレス(インターネット全体)
    # /0 = サブネットマスクが0ビット(すべてのアドレスにマッチ)
    # 例: 192.168.1.0/24 なら 192.168.1.0~192.168.1.255 の256個のアドレス
    cidr_blocks = [“0.0.0.0/0"]
  }
  # インバウンドルール(Ingress): 外部からインスタンスへの通信
  # ここでは意図的に何も定義していない(デフォルトで全て拒否)
  # SSHポート(22)も開放しないため、従来のSSH接続は不可能
  # 代わりにAWS Systems Manager (SSM) Session Managerを使用してセキュアにアクセス
}
# ========================================
# EC2インスタンス
# ========================================
# 仮想サーバー(EC2インスタンス)を作成
resource “aws_instance" “coding_agent_sandbox" {
  # 使用するAMI(OSイメージ)のID
  # 上記のデータソースで取得した最新のUbuntu 24.04を使用
  ami = data.aws_ami.ubuntu.id
  # インスタンスタイプ
  instance_type = “t3.medium"
  # IAMインスタンスプロファイル: このインスタンスに付与する権限
  # iam.tfで定義したプロファイルを使用(SSM接続に必要な権限を持つ)
  iam_instance_profile = aws_iam_instance_profile.coding_agent_sandbox_profile.name
  # セキュリティグループのID(複数指定可能なため配列形式)
  vpc_security_group_ids = [aws_security_group.coding_agent_sandbox_sg.id]
  # ルートボリューム(起動ディスク)の設定
  root_block_device {
    volume_size = 30
    volume_type = “gp3"
  }
  # SSHキーペア: null = キーペアを設定しない
  # SSM Session Managerを使用するため、SSHキーは不要
  key_name = null
}
# ========================================
# 出力(Output)
# ========================================
# terraform applyの実行後に表示される値
# 他のモジュールから参照したり、スクリプトで使用したりできる
# EC2インスタンスのID(例: i-0123456789abcdef0)
output “instance_id" {
  description = “EC2インスタンスID"
  value       = aws_instance.coding_agent_sandbox.id
}
# EC2インスタンスの状態(例: running, stopped, terminatedなど)
output “instance_state" {
  description = “EC2インスタンスの状態"
  value       = aws_instance.coding_agent_sandbox.instance_state
}

これでTerraform自体の準備は完了です。

AWS CLIのインストール

次はAWSのアカウント情報を設定するためにAWS CLIというCLI上でaws関係の操作をするソフトをインストールしましょう。
AWS CLI version 2のインストールページに従ってインストールして、以下のコマンドでインストールが無事にできているか確認しておきましょう。

which aws
aws --version

両方で出力が出ていれば問題ないです(aws --versionの初回は初期化が走るのか少々出力が遅いです)。

AWSの認証情報を登録するための準備

では、AWS CLIを使ってログインしていきましょう。
v2.32.0からはaws loginコマンドが使えるようになっているので、こちらのDevelopersIOさんの記事を参考にログインしてみましょう。

ログインが成功したらホームディレクトリに.awsというディレクトリができていると思います。
この.awsディレクトリの中のconfigファイルの中に以下を書き込みましょう。{$your_profile}の部分は好きな文字列にしましょう。また、一つ目のprofileの部分は[default]として書かれているはずなので、それを[profile {$your_profile}]と書き換えてあげましょう。

[profile {$your_profile}]
login_session = {$your_iam_info}
region = ap-northeast-1

[profile terraform-process]
credential_process = aws configure export-credentials --profile {$your_profile} --format process
region = ap-northeast-1

なんでこんなことをするのかについてですが、aws loginコマンドは2025年11月にリリースされた機能でして、現状このような明示的にcredentialをエクスポートしてあげないと使えないためです(いずれこんなことをせずとも使えるようになると思います)。

TerraformでEC2インスタンスを作成

さてさて、次はTerraformでEC2インスタンスを作成していくわけですが、あとは簡単です!
この3つのTerraformファイルを配置したディレクトリで

terraform init # 初期化
terraform validate # 構文チェック
terraform plan # dry runのような動作テスト的機能
terraform apply # 実際にインフラを作成

を順に叩いていくだけです。
これが終わればEC2インスタンスが自分のアカウント内に作成されています!

3. EC2インスタンス内にアクセスする

VSCodeからEC2インスタンスにアクセスするためには、SSH接続をしなくてはいけません。
「あれ、でもさっきSSH接続のポートは拒否したよな?」と思った方は勘がいいです。
Terraform編では、SSMを使って公開鍵認証をすることでSSH接続を行えるようにするというテクニックを使います。
それではやっていきましょう。

Session Manager Pluginをインストール

AWS CLIでEC2インスタンスにアクセスするためには、今回の設定ではSSMでの認証が必要です。
SSMでの認証をAWS CLIで使うためにはSession Manager Pluginをインストールする必要があるので、こちらのページから自分のパソコンに合うインストール方式でSession Manager Pluginをインストールしましょう。

Session Manager Pluginのインストールが正しくできているかこちらのページの以下のスクリプトで確認してみましょう。

session-manager-plugin

以下の文言が出ればOKです。

The Session Manager plugin is installed successfully. Use the AWS CLI to start a session.

VSCodeでの接続設定

本当はAWS ToolkitというVSCode拡張を使うと、だいぶ簡単にセットアップできるのですが、頻繁にセッション切れを起こす上に、再度接続もできないという謎仕様の状態なので、今回は公開鍵認証の方式を取っていきます。

まず、以下のコマンドを実行して秘密鍵公開鍵のペアを作りましょう。
パスフレーズを求められますが、Enter連打で大丈夫です。

ssh-keygen -t ed25519 -f ~/.ssh/coding_agent_sandbox

実行に成功すると、.sshディレクトリにcoding_agent_sandboxとcoding_agent_sandbox.pubという2つのファイルが出来上がっているはずです。
.pubのほうが公開鍵で、無印のほうが秘密鍵になっており、公開鍵のほうをこれからEC2インスタンスに送ります。
以下のスクリプトを適当な場所に配置して実行し、指示に従ってください(add_key_to_ec2.shとかの適当な名前で保存してください)。

#!/bin/sh
# 各変数を対話形式で入力する
# AWS CLI プロファイル名を入力
read -p “AWS CLI プロファイル名を入力してください: " profile
# まず、認証情報が生きているかどうかを確認
echo “[$profile] プロファイルの認証情報を確認しています..."
aws sts get-caller-identity --profile “$profile" >/dev/null 2>&1
if [ $? -ne 0 ]; then
  echo “認証情報が有効期限切れ、または無効のようです。AWS にログインします..."
  aws login --profile “$profile"
  # 再確認
  aws sts get-caller-identity --profile “$profile" >/dev/null 2>&1
  if [ $? -ne 0 ]; then
    echo “AWSへのログインが完了しませんでした。設定を確認してください。"
    exit 1
  fi
fi
# EC2 インスタンスIDを入力
read -p “対象EC2インスタンスのIDを入力してください: " instance_id
# AWS リージョンを入力(デフォルトは ap-northeast-1)
read -p “AWSリージョンを入力してください (デフォルト: ap-northeast-1): " region
if [ -z “$region" ]; then
  region=“ap-northeast-1"
fi
# 公開鍵ファイルのパスを入力
read -p “ホームディレクトリ以下の公開鍵ファイルのパスを入力してください (例: .ssh/aws/yourkey.pub): " pubkey_path
if [ ! -f “$HOME/$pubkey_path" ]; then
  echo “エラー: 指定された公開鍵ファイルが存在しません。"
  exit 1
fi
# 公開鍵の内容を読み込み
pubkey=$(cat “$HOME/$pubkey_path")
# EC2側のユーザー名を入力(デフォルトは ubuntu)
read -p “EC2側のユーザー名を入力してください (デフォルト: ubuntu): " ec2_username
if [ -z “$ec2_username" ]; then
  ec2_username=“ubuntu"
fi
# EC2上の指定ユーザーのauthorized_keysに公開鍵を追加するコマンドをJSON形式で作成
cmd=$(cat <<EOF
{“commands": [“mkdir -p /home/$ec2_username/.ssh", “chmod 700 /home/$ec2_username/.ssh", “echo \“$pubkey\" >> /home/$ec2_username/.ssh/authorized_keys", “chmod 600 /home/$ec2_username/.ssh/authorized_keys", “chown -R $ec2_username:$ec2_username /home/$ec2_username/.ssh"]}
EOF
)
# AWS SSM コマンドを実行して、対象インスタンスにコマンドを送信
aws ssm send-command \
  --instance-ids “$instance_id" \
  --document-name “AWS-RunShellScript" \
  --parameters “$cmd" \
  --region “$region" \
  --profile “$profile"

最後にjson形式のメッセージが出てくるので、qボタンで出ましょう。

続いて、.ssh/configファイルに追記しましょう。
以下の設定をしておくと、他のインスタンスを作った時でも流用しやすいです。

host i-* mi-*
    ProxyCommand sh -c “aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters ‘portNumber=%p’ --profile terraform-process"
    IdentityFile ~/.ssh/coding_agent_sandbox

あとは接続するだけです!
cmd+shift+pを押して(windowsではctrl+shift+p)、connect to hostと検索して出てきた一番上のRemote-SSH: ホストに接続する...を選択し、ubuntu@{$instance_id}を入力してEnterを押しましょう。
すると、インスタンスに入れます。

4. EC2インスタンスにコーディングエージェントをインストールする & 5. コーディングエージェントに暴れてもらう

ではせっかくなので、CLIインストールではなく、VSCode拡張のClaude Codeをインストールしてみましょう。

タブの拡張ボタンからClaude Codeと検索して出てきたVSCode拡張をインストールし、上部にあるClaudeの星みたいなオレンジのロゴをクリックして立ち上げてみましょう。

セットアップを終えると、使えるようになりますが、ちょっといまいちVSCode拡張の方で--dangerously-skip-permissionsを使う方法がわからなかったです。
もし強烈な権限を付与したい場合は、CLIのClaude Codeをインストールして、コンソール編でやった手順を繰り返してください。

番外編. AWSリソースを削除しよう

最後はお金がかかってしまうので、EC2インスタンスは削除していきましょう。
さて、なんといってTerraformで便利なことは以下のコマンドを叩くだけでリソースを消せることです。

terraform destroy

これだけです。
Terraformの素晴らしさが垣間見えましたね!

おまけ. コメントを無くしたTerraformファイル

provider.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 6.0"
    }
  }
}

provider "aws" {
  profile = "terraform-process"
  region  = "ap-northeast-1"
}
iam.tf
resource "aws_iam_role" "coding_agent_sandbox_role" {
  name = "coding-agent_sandbox_role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "ec2.amazonaws.com"
        }
      }
    ]
  })

  tags = {
    Name = "coding-agent_sandbox_role"
  }
}

resource "aws_iam_role_policy_attachment" "ssm_managed_instance_core" {
  role       = aws_iam_role.coding_agent_sandbox_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

resource "aws_iam_instance_profile" "coding_agent_sandbox_profile" {
  name = "coding-agent_sandbox_profile"
  role = aws_iam_role.coding_agent_sandbox_role.name
}
ec2.tf
data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"]

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }
}

resource "aws_security_group" "coding_agent_sandbox_sg" {
  name        = "coding-agent_sandbox_sg"
  description = "Security group for coding agent sandbox - no SSH access"

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "coding-agent_sandbox_sg"
  }
}

resource "aws_instance" "coding_agent_sandbox" {
  ami                    = data.aws_ami.ubuntu.id
  instance_type          = "t3.medium"
  iam_instance_profile   = aws_iam_instance_profile.coding_agent_sandbox_profile.name
  vpc_security_group_ids = [aws_security_group.coding_agent_sandbox_sg.id]

  root_block_device {
    volume_size = 30
    volume_type = "gp3"
  }

  key_name = null

  tags = {
    Name = "coding-agent_sandbox"
  }
}

output "instance_id" {
  description = "EC2インスタンスID"
  value       = aws_instance.coding_agent_sandbox.id
}

output "instance_state" {
  description = "EC2インスタンスの状態"
  value       = aws_instance.coding_agent_sandbox.instance_state
}

おわりに

書き始める前はなんか短い記事になっちゃいそうだなぁ、、、と思っていたのですが、書き始めてみるとあれよあれよと伸びていって結構な分量になってしまいました。

元々機械学習畑であって私がなぜこんな記事を書いたかというと、エクサウィザーズに入社してからクラウドインフラを勉強して、インフラ意外とおもしれ〜となったからです。また、Terraformという新しいおもちゃもなかなか楽しかったからでもあります。
そんなこんなで、AWSの全12資格中7資格を4月に入社してから取得しまして、今月も追加で2つ取って、年度内に全冠する予定です。
ちなみにTerraformを触るなら、まずは以下のTerraformで学ぶAWS(1): サーバーレスから始める再利用可能なインフラストラクチャから始めてみるとやりやすいかもしれません。
また、新刊のTerraformで学ぶAWS(2): VPC編も出ていますので、こちらもぜひ。

余談になりますが、最近は世間全体として実務では、機械学習とはいっても大体がLLMのAPIを叩くだけになってしまっていっている気がするので、LLM API芸人に飽きた機械学習畑の人は是非インフラを触ってみましょう。意外とハマるかもしれません。

エクサウィザーズ Tech Blog

Discussion