1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

EC2にSSHせずSSM Session Managerで接続するTerraform構成

1
Last updated at Posted at 2026-07-15

はじめに

個人開発アプリをAWSへ公開するためのTerraform Packを作り始めた当初、EC2への接続は普通にSSHでよいと思っていました。

ただ、Security Groupや運用手順を考えている途中で、少し引っかかりました。

「この商品を使う人全員に、SSH鍵を管理してもらうのは結構大変では?」

SSHを前提にすると、接続方法だけでも考えることが増えます。

  • 秘密鍵をどこへ保管するか
  • 22番ポートをどこまで許可するか
  • 接続元IPが変わった場合にどうするか
  • Private Subnetへ接続するために踏み台を作るか
  • 複数人で運用するとき、鍵をどう発行・失効するか
  • 秘密鍵を紛失したユーザーをどうサポートするか

もちろん、SSH自体が悪いわけではありません。

ただ、今回作っているのは個人開発者や小規模事業者向けの構成です。できるだけ管理するものを減らし、インフラに詳しくない人でも運用しやすくしたいと考えました。

そこで、EC2の管理接続にはSSHを使わず、AWS Systems Manager Session Managerを利用する構成にしました。

この記事では、SSM Session Managerを選んだ理由と、TerraformでEC2から利用できるようにする方法を紹介します。


SSM Session Managerとは

Session Managerは、AWS Systems Managerに含まれるEC2などの管理機能です。

AWS Management ConsoleやAWS CLIから、EC2へシェル接続できます。

SSHとの大きな違いは、EC2に管理接続用のインバウンドポートを開けなくても接続できることです。

AWS公式でも、Session Managerの特徴として次の点が挙げられています。

  • インバウンドポートを開けずに接続できる
  • SSH鍵を管理しなくてよい
  • 踏み台サーバーを用意しなくてよい
  • IAM Policyで接続権限を制御できる

詳細はAWS公式ドキュメントで確認できます。

今回の構成では、Webアクセスと管理接続を次のように分離しました。

ユーザーからの通信はALBを入口にし、EC2への管理接続にはSession Managerを利用します。

EC2のSecurity Groupに22番ポートは設定しません。


SSHではなくSSMにした理由

SSHポートを開けずに済む

ネットワーク経由でEC2へ直接SSHする場合、通常はSecurity Groupで22番ポートを許可します。

接続元IPを自宅などに限定すれば、公開範囲を狭めることはできます。

ただ、個人開発では外出先やテザリングから作業することもあり、接続元IPが変わるたびにSecurity Groupを変更するのは少し面倒です。

一時的に0.0.0.0/0で開けて、そのまま戻し忘れる…という経験、皆さんもあるのではないでしょうか?
私だけだったら普通に恥ずかしいなぁ。

Session Managerでは、EC2側のSSM AgentからSystems Managerへ通信するため、接続開始用のインバウンドポートを必要としません。

不要な入口を作らずに済むのが、採用した一番大きな理由です。

秘密鍵をユーザーへ管理してもらわなくてよい

SSH鍵を利用する場合は、秘密鍵の保管が必要です。

複数人で運用する場合、本来は同じ秘密鍵を共有するのではなく、利用者ごとに鍵を発行し、不要になった鍵を失効させる必要があります。

ただ、個人開発や小規模チームでそこまで運用するのは、意外と負担になります。
そして鍵の棚卸しを未来の自分に押し付けがちです。

そして商品として考えると、秘密鍵紛失による問い合わせが激増しそうでした……笑

Session Managerでは、接続する人のIAM権限を使ってアクセスを制御できます。

鍵を配布するのではなく、AWS側で「誰がどのEC2へ接続できるか」を管理できるため、今回のTerraform Packと相性がよいと判断しました。

踏み台EC2を増やしたくなかった

Private SubnetにあるEC2へSSHする方法として、Public Subnetに踏み台サーバーを配置する構成があります。

ただ、EC2へ接続するためだけに別のEC2を増やすと、管理対象も増えます。

  • 踏み台EC2の料金
  • OSアップデート
  • Security Group
  • SSH鍵
  • ログ
  • 障害監視

小規模な構成なのに、管理用サーバーの管理で忙しくなるのは本末転倒です。

Session Managerを使えば、踏み台サーバーを用意せずにPrivate Subnet上のEC2へ接続できます。

Public構成とPrivate構成で接続方法を統一できる

今回のEC2 Planには、次の2構成を含めています。

  • 低コスト構成:Public Subnet + Security Group
  • セキュア構成:Private Subnet + NAT Gateway

EC2の配置場所は異なりますが、管理接続をSession Managerへ統一すれば、利用者はどちらの構成でも同じ方法で接続できます。

aws ssm start-session `
  --target i-xxxxxxxxxxxxxxxxx `
  --region ap-northeast-1

構成によってSSH手順や鍵管理を変えなくてよいのも、運用上のメリットでした。


SSMならネットワーク設定が不要、ではない

ここは少し注意が必要です。

Session ManagerはEC2のインバウンドポートを必要としませんが、EC2からSystems Managerへ通信できる経路は必要です。

SSM Agentは、Systems ManagerのエンドポイントへHTTPSでアウトバウンド通信します。

主に次のエンドポイントが関係します。

ssm.<region>.amazonaws.com
ssmmessages.<region>.amazonaws.com
ec2messages.<region>.amazonaws.com

必要な接続先はリージョンや利用条件によって異なるため、Session Managerの前提条件も確認してください。

今回のPublic構成とPrivate構成では、通信経路が異なります。

Private Subnetからインターネットを経由させたくない場合は、Systems Manager用のVPCエンドポイントを使う方法もあります。

VPCエンドポイントを利用すれば、Internet GatewayやNAT Gatewayを通らず、AWSネットワーク内でSystems Managerへ接続できます。

詳細はAWS PrivateLinkを利用したSession Manager構成を参照してください。


必要な構成要素

EC2をSession Managerの管理対象にするには、主に次の要素が必要です。

  • SSM Agent
  • EC2用IAM Role
  • AmazonSSMManagedInstanceCore Policy
  • IAM Instance Profile
  • Systems Managerへの通信経路
  • 操作者側のIAM権限
  • AWS CLI接続時はSession Manager Plugin

Terraformでは、IAM RoleをEC2へ直接指定するのではなく、IAM Instance Profileを作成してEC2へ設定します。


EC2用IAM Roleを作成する

最初に、EC2が引き受けるIAM Roleを作成します。

resource "aws_iam_role" "ec2_ssm_role" {
  name = "sample-ec2-ssm-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"

    Statement = [
      {
        Effect = "Allow"

        Principal = {
          Service = "ec2.amazonaws.com"
        }

        Action = "sts:AssumeRole"
      }
    ]
  })

  tags = {
    Name = "sample-ec2-ssm-role"
  }
}

assume_role_policyでは、EC2サービスがこのRoleを引き受けられるようにしています。


AmazonSSMManagedInstanceCoreを付与する

作成したIAM Roleへ、AWS管理PolicyのAmazonSSMManagedInstanceCoreを付与します。

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

このPolicyには、EC2がSystems Managerの管理対象として動作するために必要な権限が含まれています。

最初から広い管理者権限を与える必要はありません。


IAM Instance Profileを作成する

次に、IAM RoleをEC2へ設定するためのInstance Profileを作成します。

resource "aws_iam_instance_profile" "ec2_ssm" {
  name = "sample-ec2-ssm-instance-profile"
  role = aws_iam_role.ec2_ssm_role.name

  tags = {
    Name = "sample-ec2-ssm-instance-profile"
  }
}

EC2リソースでは、このInstance Profileの名前を指定します。


EC2へInstance Profileを設定する

Amazon Linux 2023を利用する例です。

data "aws_ami" "amazon_linux_2023" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["al2023-ami-2023.*-x86_64"]
  }

  filter {
    name   = "architecture"
    values = ["x86_64"]
  }

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

resource "aws_instance" "app" {
  ami                    = data.aws_ami.amazon_linux_2023.id
  instance_type          = "t3.micro"
  subnet_id              = aws_subnet.app.id
  vpc_security_group_ids = [aws_security_group.app.id]

  iam_instance_profile = aws_iam_instance_profile.ec2_ssm.name

  metadata_options {
    http_endpoint = "enabled"
    http_tokens   = "required"
  }

  tags = {
    Name = "sample-app"
  }

  depends_on = [
    aws_iam_role_policy_attachment.ssm
  ]
}

重要なのは次の部分です。

iam_instance_profile = aws_iam_instance_profile.ec2_ssm.name

これで、EC2がInstance Profile経由でIAM Roleを利用できるようになります。

metadata_optionsでは、Instance Metadata Service Version 2を必須にしています。


Security GroupにSSHを追加しない

アプリケーション用Security Groupには、22番ポートを設定しません。

ALBを利用する場合は、アプリケーションへのHTTP通信をALBのSecurity Groupからだけ許可します。

resource "aws_security_group" "app" {
  name   = "sample-app-sg"
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "sample-app-sg"
  }
}

resource "aws_vpc_security_group_ingress_rule" "app_from_alb" {
  security_group_id = aws_security_group.app.id

  referenced_security_group_id = aws_security_group.alb.id

  from_port   = 80
  to_port     = 80
  ip_protocol = "tcp"

  description = "Allow HTTP only from ALB"
}

resource "aws_vpc_security_group_egress_rule" "app_outbound" {
  security_group_id = aws_security_group.app.id

  cidr_ipv4   = "0.0.0.0/0"
  ip_protocol = "-1"

  description = "Allow outbound traffic"
}

このSecurity Groupには、次のようなSSH用ルールは作成しません。

# 今回は作成しない
#
# resource "aws_vpc_security_group_ingress_rule" "ssh" {
#   security_group_id = aws_security_group.app.id
#   cidr_ipv4         = "0.0.0.0/0"
#   from_port         = 22
#   to_port           = 22
#   ip_protocol       = "tcp"
# }

WebアクセスはALBからだけ、管理接続はSession Managerから行う、という役割分担です。


SSM Agentを確認する

AWSが提供するAmazon Linux 2やAmazon Linux 2023のAMIでは、多くの場合SSM Agentが最初からインストールされています。

ただし、AMIによって異なる可能性があるため、「Amazon Linuxだから必ず動く」と決めつけず、状態を確認します。

EC2上では次のコマンドで確認できます。

sudo systemctl status amazon-ssm-agent

停止している場合は起動します。

sudo systemctl enable --now amazon-ssm-agent

AWS公式にも、Amazon Linux 2とAmazon Linux 2023では通常SSM Agentがプリインストールされていると記載されています。

Amazon Linux 2/Amazon Linux 2023へのSSM Agent導入


AWS CLIから接続する

ローカルPCから接続する場合は、AWS CLIとSession Manager Pluginをインストールします。

対象EC2のInstance IDを確認したら、次のコマンドを実行します。

aws ssm start-session `
  --target i-xxxxxxxxxxxxxxxxx `
  --region ap-northeast-1

接続に成功すると、EC2上のシェルを操作できます。

Starting session with SessionId: user-xxxxxxxxxxxxxxxxx
sh-5.2$

TerraformのOutputとしてコマンドを出しておくと、利用者がInstance IDを探す手間を減らせます。

output "ssm_start_session_command" {
  description = "Command to connect to the EC2 instance using Session Manager"

  value = join(" ", [
    "aws ssm start-session",
    "--target ${aws_instance.app.id}",
    "--region ${var.aws_region}",
  ])
}

構築後は次のように確認できます。

terraform output -raw ssm_start_session_command

操作者側にもIAM権限が必要

EC2にIAM Roleを付けるだけでは、誰でも接続できるわけではありません。

Session Managerを開始するIAMユーザーやIAM Roleにも、対象EC2へのセッション開始を許可する権限が必要です。

設計上は、次の2つを分けて考えます。

  • EC2側の権限:SSM AgentがSystems Managerと通信するための権限
  • 操作者側の権限:対象EC2に対してSession Managerを開始する権限

開発者全員へAdministratorAccessを付けるのではなく、接続対象や操作範囲をIAM Policyで制限するのが理想です。

複数人で運用する場合は、IAM Identity CenterやIAM Roleと組み合わせる方法もあります。


接続できないときに確認すること

Session Managerで接続できない場合、よくあるのが次の4点です。

1. IAM Instance Profileが付いていない

EC2のIAM Role欄とTerraform stateを確認します。

terraform state show aws_instance.app

2. AmazonSSMManagedInstanceCoreが付いていない

IAM RoleにPolicyが付与されているか確認します。

aws iam list-attached-role-policies `
  --role-name sample-ec2-ssm-role

3. SSM Agentが停止している

EC2上でAgentの状態を確認します。

sudo systemctl status amazon-ssm-agent

4. Systems Managerへ通信できない

Private Subnetの場合は特に注意が必要です。

  • NAT Gatewayへのルートがあるか
  • VPCエンドポイントを利用しているか
  • 443番のアウトバウンド通信が許可されているか
  • VPCエンドポイントのSecurity Groupが正しいか

AWS CLIから管理対象として認識されているかも確認できます。

aws ssm describe-instance-information `
  --region ap-northeast-1

対象EC2が表示されなければ、まずEC2側のIAM Role、SSM Agent、通信経路を確認します。


SSM Session Managerも万能ではない

Session Managerを使えば、SSH鍵や踏み台サーバーを減らせます。

ただし、設定なしで安全になるわけではありません。

  • 操作者のIAM権限を適切に制限する
  • 不要になった権限を削除する
  • SSM Agentを更新する
  • Systems Managerへの通信経路を確認する
  • 必要に応じてセッションログを記録する
  • CloudTrailでAPI操作を追跡できるようにする

特に複数人で利用する場合は、「接続できるか」だけでなく、「誰が接続できるか」「操作をどこまで記録するか」まで考える必要があります。


まとめ

今回のTerraform Packでは、EC2への管理接続をSSM Session Managerへ統一しました。

理由はシンプルです。

  • 22番ポートを開けたくない
  • SSH秘密鍵を購入者へ管理してもらいたくない。というかサポート対応したくない
  • 踏み台EC2を増やしたくない
  • Public構成とPrivate構成で接続方法を統一したい
  • IAMで接続権限を管理したい

実装上のポイントは次のとおりです。

1. EC2用IAM Roleを作成する
2. AmazonSSMManagedInstanceCoreを付与する
3. IAM Instance Profileを作成する
4. EC2へInstance Profileを設定する
5. SSM Agentを起動する
6. Systems Managerへのアウトバウンド通信を確保する
7. 操作者側へSession Managerの権限を付与する
8. Security Groupへ22番ポートを追加しない

SSMを採用したことで、SSH鍵を配らず、踏み台も増やさず、不要なインバウンドポートを開けない構成にできました。

個人開発でも、公開する以上は最低限守りたいところがあります。

一方で、セキュリティを理由に管理対象を増やしすぎると、今度は運用できなくなります。

その間を取る方法として、SSM Session Managerはかなり使いやすい選択肢でした。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?