publicサブネットに配置したAWS EC2インスタンスにssmでログインする
はじめに
AWS Systems Manager Session Managerを使用すると、SSHキーを管理することなくEC2インスタンスにログインできます。
個人的な勉強でEC2インスタンスを使用するとき、毎回SSHキーの設定をしていました。
しかし今回はEC2インスタンスをpublicサブネットに配置し、Session Managerでアクセスできる状態をTerraformで作成してみます。
課題
- EC2インスタンスにSSHするために以下の手順が発生
- SSHキーを作成
- キーのダウンロード
- ssh agentに鍵を登録
- 踏み台サーバとして利用するときは、グローバルIPに合わせてセキュリティグループのインバウンドルールを修正しなければいけない
目標
この記事ではゴールを以下に設定します。
- AWSマネジメントコンソールからEC2インスタンスへセッションを開始できる
- ローカルPCから
aws ssm session-start
コマンドを実行できるようにする
前提
- ローカルでterraformコマンドを実行できる
- ローカルでaws cliを実行できる
~/.aws/credentials
にAWSアクセスキーを設定している
バージョン
ツール | バージョン |
---|---|
terraform | v1.4.6 |
aws cli | aws-cli/2.9.19 |
準備
環境変数
環境変数をエクスポートします。
筆者はdirenvを使っているため.env
ファイルを定義します。
AWS_PROFILE
の値は、~/.aws/credentials
に合わせて設定してください。
$ cat ~/.aws/credentials[profile_name]aws_access_key_id = xxxxxxxxxxaws_secret_access_key = xxxxxxxxxx
AWS_PROFILE=profile_nameAWS_REGION=ap-northeast-1
Terraformのバージョンを指定する
使用するTerraformのバージョンは2023年5月現在最新の1.4.6を使用します。
$ asdf list all terraform | tail1.4.11.4.21.4.31.4.41.4.51.4.61.5.0-alpha202304051.5.0-alpha202305041.5.0-beta11.5.0-beta2
$ asdf local terraform 1.4.6 # .tool-versions作成$ terraform versionTerraform v1.4.6on darwin_arm64
terraform init
aws providerのバージョンも2023年5月現在、最新のバージョンにします。
terraform { required_version = "1.4.6"
required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0.1" } }}
provider "aws" { region = "ap-northeast-1"}
tfstateはあくまで勉強用なのでs3ではなくローカルに置くことにします。
terraform { backend "local" { path = "./terraform.tfstate" }}
terraform initを実行してみます。
$ terraform init
Initializing the backend...
Initializing provider plugins...- Reusing previous version of hashicorp/aws from the dependency lock file- Using previously-installed hashicorp/aws v5.0.1
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to seeany changes that are required for your infrastructure. All Terraform commandsshould now work.
If you ever set or change modules or backend configuration for Terraform,rerun this command to reinitialize your working directory. If you forget, othercommands will detect it and remind you to do so if necessary.
とりあえず準備は完了しました。この段階でmain.tfには何も書いていませんが、ここまで以下のようなディレクトリ構成になっています。
$ tree -a -L 1.├── .env├── .gitignore├── .terraform├── .terraform.lock.hcl├── .tool-versions├── backend.tf├── main.tf└── providers.tf
ネットワークを整える
まずはVPC周辺のリソースを先に作成します。
resource "aws_vpc" "ssm_vpc" { cidr_block = "172.16.0.0/16"
tags = { Name = "aws-ssm-example" }}
resource "aws_subnet" "ssm_public_subnet" { vpc_id = aws_vpc.ssm_vpc.id cidr_block = "172.16.10.0/24" availability_zone = "ap-northeast-1a"
tags = { Name = "ssm-public-subnet" }}
resource "aws_internet_gateway" "example_igw" { vpc_id = aws_vpc.ssm_vpc.id
tags = { Name = "ssm-example-igw" }}
resource "aws_route_table" "example_rt" { vpc_id = aws_vpc.ssm_vpc.id
route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.example_igw.id }
tags = { Name = "ssm-example-rt" }}
resource "aws_route_table_association" "ssm_association" { subnet_id = aws_subnet.ssm_public_subnet.id route_table_id = aws_route_table.example_rt.id}
resource "aws_security_group" "ssm_public_sg" { name = "ssm-public-sg" description = "ssm public security group" vpc_id = aws_vpc.ssm_vpc.id
egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] ipv6_cidr_blocks = ["::/0"] }
tags = { Name = "ssm-public-sg" }}
EC2インスタンスのAMIを探す
仕事ではAmazon Linux2を使用していますが、勉強目的なので今回はAmazon Linux 2023を使用します。
Amazon Linux 2023 (AL2023) のAMIにはすでに AWS System Managerエージェント(SSM Agent)がインストールされています。
参考:
- SSM Agent がプリインストールされた Amazon Machine Images (AMIs) - AWS Documentation
- Amazon Linux 2023 に関するよくある質問
ネット上に存在するAmazon Linux 2023の記事やamazonlinux/amazon-linux-2023 - GitHubをみると、AMI名はal2023-ami-xxxxx
になっていることが確認できます。
Terraformだとdata sourcesを使うことでAMI IDを確認できます。
data "aws_ami" "amzn-linux-2023-ami" { most_recent = true owners = ["amazon"]
filter { name = "name" values = ["al2023-ami-2023.*-x86_64"] }}
output "al2023" { value = data.aws_ami.amzn-linux-2023-ami}
terraform plan
$ terraform plandata.aws_ami.amzn-linux-2023-ami: Reading...data.aws_ami.amzn-linux-2023-ami: Read complete after 0s [id=ami-03dceaabddff8067e]
Changes to Outputs: + al2023 = { + architecture = "x86_64" + arn = "arn:aws:ec2:ap-northeast-1::image/ami-03dceaabddff8067e" + block_device_mappings = [ + { + device_name = "/dev/xvda" + ebs = { + delete_on_termination = "true" + encrypted = "false" + iops = "3000" + snapshot_id = "snap-0c81aad031eccf0b3" + throughput = "125" + volume_size = "8" + volume_type = "gp3" } + no_device = "" + virtual_name = "" }, ] + boot_mode = "uefi-preferred" + creation_date = "2023-05-15T20:19:40.000Z" + deprecation_time = "2023-08-13T20:20:00.000Z" + description = "Amazon Linux 2023 AMI 2023.0.20230517.1 x86_64 HVM kernel-6.1" + ena_support = true + executable_users = null + filter = [ + { + name = "name" + values = [ + "al2023-ami-2023.*-x86_64", ] }, ] + hypervisor = "xen" + id = "ami-03dceaabddff8067e" + image_id = "ami-03dceaabddff8067e" + image_location = "amazon/al2023-ami-2023.0.20230517.1-kernel-6.1-x86_64" + image_owner_alias = "amazon" + image_type = "machine" + imds_support = "v2.0" + include_deprecated = false + kernel_id = "" + most_recent = true + name = "al2023-ami-2023.0.20230517.1-kernel-6.1-x86_64" + name_regex = null + owner_id = "137112412989" + owners = [ + "amazon", ] + platform = "" + platform_details = "Linux/UNIX" + product_codes = [] + public = true + ramdisk_id = "" + root_device_name = "/dev/xvda" + root_device_type = "ebs" + root_snapshot_id = "snap-0c81aad031eccf0b3" + sriov_net_support = "simple" + state = "available" + state_reason = { + code = "UNSET" + message = "UNSET" } + tags = {} + timeouts = null + tpm_support = "" + usage_operation = "RunInstances" + virtualization_type = "hvm" }
You can apply this plan to save these new output values to the Terraform state, without changing any real infrastructure.
aws cliからAMI IDを探す
Systems Manager を使用して最新の Amazon Linux AMI を検索するにはaws cliにあるaws ssm get-parameters-by-pathコマンドを実行すると取得できます。
aws ssm get-parameters-by-path
$ aws ssm get-parameters-by-path --path /aws/service/ami-amazon-linux-latest --query "Parameters[?contains(Name,\`al2023\`)].{Name: Name,Value:Value}"[ { "Name": "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-arm64", "Value": "ami-0ebbfa73436e5e751" }, { "Name": "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64", "Value": "ami-03dceaabddff8067e" }, { "Name": "/aws/service/ami-amazon-linux-latest/al2023-ami-minimal-kernel-6.1-arm64", "Value": "ami-0283f118a0c8175da" }, { "Name": "/aws/service/ami-amazon-linux-latest/al2023-ami-minimal-kernel-6.1-x86_64", "Value": "ami-092e2a393f8606a75" }, { "Name": "/aws/service/ami-amazon-linux-latest/al2023-ami-minimal-kernel-default-arm64", "Value": "ami-0283f118a0c8175da" }, { "Name": "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64", "Value": "ami-03dceaabddff8067e" }, { "Name": "/aws/service/ami-amazon-linux-latest/al2023-ami-minimal-kernel-default-x86_64", "Value": "ami-092e2a393f8606a75" }, { "Name": "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64", "Value": "ami-0ebbfa73436e5e751" }]
EC2インスタンスを作成する
ドキュメントによるとAmazonSSMManagedInstanceCore
ポリシーを持ったロールをアタッチする必要があるそうです。
Session Manager に必要な基本許可を持つインスタンスプロファイルがすでにAmazon EC2インスタンスに添付されている可能性があります。AWS 管理ポリシー AmazonSSMManagedInstanceCore を含むインスタンスプロファイルが既にインスタンスにアタッチされている場合、Session Manager に必要なアクセス許可は既に付与されています。
TerraformでIAMロール、インスタンスプロファイル、EC2インスタンスを作成します。
コードは先ほどの続きで、main.tf に追記します。
data "aws_ami" "amzn-linux-2023-ami" { most_recent = true owners = ["amazon"]
filter { name = "name" values = ["al2023-ami-2023.*-x86_64"] }}
resource "aws_instance" "ssm" { ami = data.aws_ami.amzn-linux-2023-ami.id instance_type = "t2.micro" subnet_id = aws_subnet.ssm_public_subnet.id iam_instance_profile = aws_iam_instance_profile.ssm_instance_core.name tags = { Name = "aws-ssm-punlic-example" }
associate_public_ip_address = true}
data "aws_iam_policy_document" "instance_assume_role_policy" { statement { actions = ["sts:AssumeRole"]
principals { type = "Service" identifiers = ["ec2.amazonaws.com"] } }}
data "aws_iam_policy" "ssm_managed_instance_core" { arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"}
resource "aws_iam_role" "ssm_instance_core" { name = "SSMInstanceCoreRole" assume_role_policy = data.aws_iam_policy_document.instance_assume_role_policy.json managed_policy_arns = [data.aws_iam_policy.ssm_managed_instance_core.arn]}
resource "aws_iam_instance_profile" "ssm_instance_core" { name = "ssm" role = aws_iam_role.ssm_instance_core.name}
terraform plan、terraform applyコマンドを実行するとEC2インスタンスの作成できます。
接続
AWSマネジメントコンソールからアクセスする
早速 EC2のページからインスタンスを選択して接続
ボタンをクリックしてみます。
publicサブネットに配置した EC2インスタンスは接続ボタンが押せるようになっています!
ボタンをクリックするとAWSマネジメントコンソールからセッションを開始することができました!
これで1つ目の目標 “AWSマネジメントコンソールからEC2インスタンスへセッションを開始できる” が完了しました。
ターミナルからセッションを開始する
aws ssm start-session
コマンドでセッションを開始するには、Session Manegerプラグインをインストール必要があります。
$ aws ssm start-session --target i-00b32e54399466f12SessionManagerPlugin is not found. Please refer to SessionManager Documentation here: http://docs.aws.amazon.com/console/systems-manager/session-manager-plugin-not-found
$ curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/mac_arm64/sessionmanager-bundle.zip" -o "sessionmanager-bundle.zip"$ unzip sessionmanager-bundle.zip$ sudo ./sessionmanager-bundle/install -i /usr/local/sessionmanagerplugin -b /usr/local/bin/session-manager-plugin
参考:AWS CLI 用の Session Manager プラグインをインストールする
無事にセッションをスタートさせることができました。
これで2つ目の目標 “ローカルPCからaws ssm session-start
コマンドを実行できるようにする” が完了しました。
$ aws ssm start-session --target i-00b32e54399466f12
Starting session with SessionId: xxxxxx-0b606847d50d67039sh-5.2$
さいごに
今回はpublicサブネットに EC2インスタンスを配置しました。
しかし、実際の運用ではprivateサブネットに置くことの方が多いと思います。
次回はprivateサブネットに配置した EC2インスタンスにアクセスできるようにしてみようと思います。
使ったコードは blog-code/2023/05/aws-ssm-public に置いています。