SSMセッションマネージャーでEC2タグベースのアクセス制御を試してみた

SSMセッションマネージャーでEC2タグベースのアクセス制御を試してみた

2026.06.21

はじめに

クラウド事業本部、あきやまです。

特定のEC2インスタンスにだけSSM接続を許可したいっていうことってありませんか?私はあります。
そこで、EC2のタグとIAMポリシーの条件キーを組み合わせたアクセス制御を検証してみました。

環境

項目
OS macOS Sequoia
Terraform >= 1.0
AWS Provider ~> 5.0
リージョン ap-northeast-1
EC2 AMI Amazon Linux 2023(最新)
インスタンスタイプ t3.micro

やってみた

全体構成

今回の検証環境はTerraformで構築しました。構成は以下のとおりです。

  • プライベートサブネットにEC2を配置(パブリックIP なし)
  • VPCエンドポイント経由でSSM接続(NAT Gateway 不要)
  • IAMグループにアタッチしたポリシーでタグベースのアクセス制御

Step 1: VPCとVPCエンドポイントの作成

プライベートサブネットからSSMに接続するため、3つのVPCエンドポイントを作成します。

vpc.tf
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name    = "${var.project}-vpc"
    Project = var.project
  }
}

resource "aws_subnet" "private" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = data.aws_availability_zones.available.names[0]

  tags = {
    Name    = "${var.project}-private"
    Project = var.project
  }
}

VPCエンドポイントはSSMセッションマネージャーに必要な3つを作成します。

vpc.tf
resource "aws_vpc_endpoint" "ssm" {
  vpc_id              = aws_vpc.main.id
  service_name        = "com.amazonaws.${var.region}.ssm"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = [aws_subnet.private.id]
  security_group_ids  = [aws_security_group.vpce.id]
  private_dns_enabled = true

  tags = {
    Name = "${var.project}-ssm"
  }
}

resource "aws_vpc_endpoint" "ssmmessages" {
  vpc_id              = aws_vpc.main.id
  service_name        = "com.amazonaws.${var.region}.ssmmessages"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = [aws_subnet.private.id]
  security_group_ids  = [aws_security_group.vpce.id]
  private_dns_enabled = true

  tags = {
    Name = "${var.project}-ssmmessages"
  }
}

resource "aws_vpc_endpoint" "ec2messages" {
  vpc_id              = aws_vpc.main.id
  service_name        = "com.amazonaws.${var.region}.ec2messages"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = [aws_subnet.private.id]
  security_group_ids  = [aws_security_group.vpce.id]
  private_dns_enabled = true

  tags = {
    Name = "${var.project}-ec2messages"
  }
}

VPCエンドポイント用のセキュリティグループは、VPC内からの443ポートを許可します。

vpc.tf
resource "aws_security_group" "vpce" {
  name   = "${var.project}-vpce"
  vpc_id = aws_vpc.main.id

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = [aws_vpc.main.cidr_block]
  }

  tags = {
    Name = "${var.project}-vpce"
  }
}

Step 2: EC2インスタンスの作成

EC2にはSSMエージェント用のIAMロールと、アクセス制御に使う SSMAccess=allowed タグを付与します。

ec2.tf
data "aws_ssm_parameter" "al2023_ami" {
  name = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64"
}

resource "aws_instance" "target" {
  ami                    = data.aws_ssm_parameter.al2023_ami.value
  instance_type          = "t3.micro"
  subnet_id              = aws_subnet.private.id
  iam_instance_profile   = aws_iam_instance_profile.ec2.name
  vpc_security_group_ids = [aws_security_group.ec2.id]

  tags = {
    Name      = "${var.project}-target"
    Project   = var.project
    SSMAccess = "allowed"
  }
}

EC2用のセキュリティグループは、VPCエンドポイントへの通信(443)のみ許可します。

ec2.tf
resource "aws_security_group" "ec2" {
  name   = "${var.project}-ec2"
  vpc_id = aws_vpc.main.id

  egress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = [aws_vpc.main.cidr_block]
  }

  tags = {
    Name = "${var.project}-ec2"
  }
}

EC2にアタッチするIAMロールには、SSMエージェントの動作に必要な AmazonSSMManagedInstanceCore マネージドポリシーを付与します。

iam.tf
resource "aws_iam_role" "ec2" {
  name = "${var.project}-ec2-role"

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

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

resource "aws_iam_instance_profile" "ec2" {
  name = "${var.project}-ec2-profile"
  role = aws_iam_role.ec2.name
}

Step 3: タグベースアクセス制御のIAMポリシー作成

ここが今回の本題です。IAMグループを作成し、タグ条件付きのポリシーをアタッチします。

iam.tf
resource "aws_iam_group" "ssm_access" {
  name = "${var.project}-ssm-access"
}

resource "aws_iam_group_policy" "ssm_access" {
  name  = "${var.project}-ssm-start-session"
  group = aws_iam_group.ssm_access.name

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "AllowStartSession"
        Effect = "Allow"
        Action = "ssm:StartSession"
        Resource = "arn:aws:ec2:${var.region}:*:instance/*"
        Condition = {
          StringLike = {
            "ssm:resourceTag/SSMAccess" = "allowed"
          }
        }
      },
      {
        Sid    = "AllowSessionDocument"
        Effect = "Allow"
        Action = "ssm:StartSession"
        Resource = [
          "arn:aws:ssm:${var.region}::document/SSM-SessionManagerRunShell",
          "arn:aws:ssm:${var.region}:*:document/SSM-SessionManagerRunShell"
        ]
      },
      {
        Sid    = "AllowDescribe"
        Effect = "Allow"
        Action = [
          "ssm:DescribeSessions",
          "ssm:DescribeInstanceProperties",
          "ssm:GetConnectionStatus",
          "ec2:DescribeInstances"
        ]
        Resource = "*"
      },
      {
        Sid    = "AllowSessionManagement"
        Effect = "Allow"
        Action = [
          "ssm:TerminateSession",
          "ssm:ResumeSession"
        ]
        Resource = "arn:aws:ssm:*:*:session/$${aws:username}-*"
      }
    ]
  })
}

ポリシーは4つのStatementで構成されています。

AllowStartSession — タグベースアクセス制御の核です。ssm:resourceTag/SSMAccessallowed であるインスタンスに対してのみ ssm:StartSession を許可します。

AllowSessionDocumentssm:StartSession はインスタンスだけでなく、セッションで使用するSSMドキュメントに対しても許可が必要です。SSM-SessionManagerRunShell はデフォルトのセッションドキュメントで、このStatementがないとセッション開始時にアクセス拒否になります。

AllowDescribe — マネジメントコンソールやCLIでインスタンス一覧・セッション状態を取得するために必要です。これらのDescribe系アクションはリソースレベルの絞り込みができないため、Resource: "*" を指定します。

AllowSessionManagement — 自分が開始したセッションの終了・再開を許可します。${aws:username} により、他のユーザーのセッションは操作できません。

Step 4: デプロイと動作確認

Terraformでデプロイします。

terraform init
terraform apply

デプロイ完了後、動作確認を行います。
GUIから確認したところ問題なく接続できることを確認しました。
スクリーンショット 2026-06-21 15.32.18

EC2に付与されたタグを変更します。
スクリーンショット 2026-06-21 15.39.19

再度、GUIから接続を試みたところエラーが表示され接続できないことを確認しました。
スクリーンショット 2026-06-21 15.37.10

注意点・制約

Describe系アクションも必要

ssm:StartSession だけでは不十分です。マネジメントコンソールやCLIでの操作には以下のアクションも必要になります。

  • ssm:DescribeSessions — セッション一覧の取得
  • ssm:DescribeInstanceProperties — インスタンスのSSM情報取得
  • ssm:GetConnectionStatus — 接続状態の確認
  • ec2:DescribeInstances — EC2インスタンス情報の取得

これらはリソースレベルの絞り込みができないため、Resource: "*" の指定が必要です。

タグの変更には注意

EC2のタグを変更するだけでアクセス権が変わります。タグの変更権限(ec2:CreateTagsec2:DeleteTags)の管理も合わせて検討してください。

まとめ

EC2タグとIAMポリシーの条件キーを組み合わせることで、SSMセッションマネージャーのアクセス制御を実現できました。

  • ssm:resourceTag の条件キーでインスタンス単位のアクセス制御が可能
  • IAMグループとの組み合わせで、ユーザーの追加・削除だけで権限管理できる
  • VPCエンドポイント経由ならNAT Gateway不要でコスト削減にもなる

参考になれば幸いです。

参考

この記事をシェアする

関連記事