VS Code を利用しながら、EC2インスタンスコネクトを利用してセッションマネージャー経由で接続してみた
こんにちは!アノテーション AWS テクニカルサポートチームの大高です。
EC2 インスタンスへの接続方法の1つとして、インスタンスコネクトとセッションマネージャを利用した接続方法があります。
EC2 インスタンスに対して「EC2 インスタンスコネクトを利用してセッションマネージャ経由で接続」をすると、以下のメリットがあるため私はよくこの方法を利用しています。
- SSH で接続ができる(公開鍵・秘密鍵はローカル端末で生成して使い捨てることも出来る)
- プライベートネットワーク上のインスタンスに接続できる
- 単純にセッションマネージャを利用して作業している場合、セッションが切れた場合に作業内容が失われるが、SSH の場合だとセッションが維持される
- SSH 接続をすることで、VS Code の Remote Development が利用できる
とくに、わたしは「VS Code の Remote Development が利用できる」ことで、EC2 インスタンス上のファイルを VS Code のエクスプローラから開いたり、VS Code 上でターミナルを複数同時に開いたりすることが出来るので、よく利用しています。
そこで、実際の接続方法を以下にまとめておきたいと思います。
前提
以下のエントリで紹介されている方法を組み合わせて対応します。
エントリ内で書かれているような、IAM ユーザーへのポリシー付与や、接続先インスタンスのセッションマネージャ設定などは済まされていることを前提とします。
公開鍵・秘密鍵の作成
まずは利用する公開鍵・秘密鍵を作成します。
ここではキー名を deploy_rsa
としました。また、パスフレーズ( -N
オプション)は空文字(省略)としています。
$ cd ~/.ssh
$ ssh-keygen -f deploy_rsa -N ""
また、併せてファイルの権限を read のみにしておきます。
# 権限設定
$ chmod 400 deploy_rsa*
# 権限確認
$ ls -l
total 72
-r-------- 1 ootaka.daisuke staff 419 4 9 17:20 deploy_rsa
-r-------- 1 ootaka.daisuke staff 110 4 9 17:20 deploy_rsa.pub
~/.ssh/config ファイルの設定
次にアクセスしたい EC2 インスタンスへ SSH 接続するための設定を、 ~/.ssh/config
へ明示的に記述しておきます。
CLI から SSH 接続するための設定は、以下のエントリにある汎用的な設定( host i-* mi-*
への設定 )が良いのですが、今回のケースでは VS Code を併用するため、明示的に記述します。
設定例としては、以下のような感じです。
Host deploy-server
HostName i-xxxxxxxxxxxxxxxxx
Port 22
User ec2-user
IdentityFile /Users/foo.bar/.ssh/deploy_rsa
ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p' --profile deploy"
IdentityFile
に、先ほど作成した秘密鍵へのパスを記載しています。
また、 ProxyCommand
にはセッションマネージャを利用して接続するようにコマンドを記述しています。今回の例ではプロファイル指定もしたかったので、 --profile deploy
のようにプロファイル指定をしています。
Instance Connect で公開鍵を送る処理をスクリプト化しておく
あとは Instance Connect で公開鍵を送り、すばやく VS Code で SSH 接続するだけなのですが、
手間を省くために Instance Connect で公開鍵を送る処理をスクリプト化しておきます。
私は以下のようなシェルスクリプトを作成して利用しています。
# シェルスクリプト作成
$ touch send-ssh-public-key.sh
#!/bin/bash
AWS_PROFILE=deploy
INSTANCE_ID=i-xxxxxxxxxxxxxxxxx # i-xxxxxxxxxxxxxxxxx (deploy-server)
SSH_KEY_NAME=deploy_rsa
SSH_PRIVATE_KEY=${HOME}/.ssh/${SSH_KEY_NAME}
SSH_PUBLIC_KEY=${HOME}/.ssh/${SSH_KEY_NAME}.pub
aws ec2-instance-connect send-ssh-public-key \
--profile ${AWS_PROFILE} \
--region ap-northeast-1 \
--instance-id ${INSTANCE_ID} \
--instance-os-user ec2-user \
--ssh-public-key file://${SSH_PUBLIC_KEY}
# シェルスクリプトに実行権限を付与
$ chmod 755 send-ssh-public-key.sh
EC2インスタンスを用意する
今回は例として、以下のような簡単な CloudFormation テンプレートを利用してEC2インスタンスを用意してみました。
AWSTemplateFormatVersion: "2010-09-09"
Description: "Simple Private Server Template."
Parameters:
# ------------------------------------------------------------#
# Common
# ------------------------------------------------------------#
Prefix:
Type: String
Default: "prefix"
# ------------------------------------------------------------#
# Network
# ------------------------------------------------------------#
VpcCidr:
Type: String
Default: "10.0.0.0/16"
PrivateSubnetCidr:
Type: String
Default: "10.0.0.0/24"
# ------------------------------------------------------------#
# EC2
# ------------------------------------------------------------#
EC2InstanceName:
Type: String
Default: "ec2"
EC2InstanceAMI:
Type: AWS::EC2::Image::Id
Default: "ami-0b6e7ccaa7b93e898" # Amazon Linux 2023 AMI 2023.7.20250331.0 x86_64 HVM kernel-6.1
EC2InstanceInstanceType:
Type: String
Default: "t3.nano"
EC2InstanceVolumeType:
Type: String
Default: "gp3"
EC2InstanceVolumeSize:
Type: String
Default: "8"
Resources:
# ------------------------------------------------------------#
# Network
# ------------------------------------------------------------#
Vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCidr
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Sub ${Prefix}-vpc
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: !Ref PrivateSubnetCidr
VpcId: !Ref Vpc
AvailabilityZone:
Fn::Select:
- "0"
- Fn::GetAZs: ""
Tags:
- Key: Name
Value: !Sub ${Prefix}-private-subnet
SecurityGroup:
Type: "AWS::EC2::SecurityGroup"
Properties:
VpcId: !Ref Vpc
GroupName: !Sub "${Prefix}-sg"
GroupDescription: "-"
Tags:
- Key: "Name"
Value: !Sub "${Prefix}-sg"
SsmVpcEndpointSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: !Sub ${Prefix}-ssm-vpc-endpoint-sg
GroupName: !Sub ${Prefix}-ssm-vpc-endpoint-sg
VpcId: !Ref Vpc
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
SourceSecurityGroupId: !Ref SecurityGroup
Tags:
- Key: Name
Value: !Sub ${Prefix}-ssm-vpc-endpoint-sg
SsmVpcEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcEndpointType: Interface
PrivateDnsEnabled: true
ServiceName: !Sub com.amazonaws.${AWS::Region}.ssm
VpcId: !Ref Vpc
SubnetIds:
- !Ref PrivateSubnet
SecurityGroupIds:
- !Ref SsmVpcEndpointSecurityGroup
SsmMessagesVpcEndpointSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: !Sub ${Prefix}-ssmmessages-vpc-endpoint-sg
GroupName: !Sub ${Prefix}-ssmmessages-vpc-endpoint-sg
VpcId: !Ref Vpc
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
SourceSecurityGroupId: !Ref SecurityGroup
Tags:
- Key: Name
Value: !Sub ${Prefix}-ssmmessages-vpc-endpoint-sg
SsmMessagesVpcEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
VpcEndpointType: Interface
PrivateDnsEnabled: true
ServiceName: !Sub com.amazonaws.${AWS::Region}.ssmmessages
VpcId: !Ref Vpc
SubnetIds:
- !Ref PrivateSubnet
SecurityGroupIds:
- !Ref SsmMessagesVpcEndpointSecurityGroup
# ------------------------------------------------------------#
# Ec2InstanceProfile
# ------------------------------------------------------------#
Ec2Role:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${Prefix}-ec2-role
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
Ec2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
InstanceProfileName: !Sub ${Prefix}-ec2-instance-profile
Roles:
- !Ref Ec2Role
# ------------------------------------------------------------#
# EC2Instance
# ------------------------------------------------------------#
EC2Instance:
Type: "AWS::EC2::Instance"
Properties:
Tags:
- Key: Name
Value: !Sub "${Prefix}-${EC2InstanceName}"
ImageId: !Ref EC2InstanceAMI
InstanceType: !Ref EC2InstanceInstanceType
IamInstanceProfile: !Ref Ec2InstanceProfile
DisableApiTermination: false
EbsOptimized: false
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
DeleteOnTermination: true
VolumeType: !Ref EC2InstanceVolumeType
VolumeSize: !Ref EC2InstanceVolumeSize
SecurityGroupIds:
- !Ref SecurityGroup
SubnetId: !Ref PrivateSubnet
UserData: !Base64 |
#! /bin/bash
yum update -y
接続してみる
あとは実際に接続するだけです。
まずは、先程作成したシェルスクリプトを実行して公開鍵を EC2 インスタンスへ送ります。
$ ./send-ssh-public-key.sh
公開鍵を送ったら、VS Code 左下の ><
アイコンの箇所をクリックし、「ホストに接続する... - Remote SSH」を選択します。
すると、設定ファイル ~/.ssh/config
に記載した deploy-server
がリストに出てくるので、そのままクリックします。
必要に応じて本当に接続してよいかなどを聞かれます。問題なければ Yes
として進めて、最後に /home/ec2-user
フォルダを VS Code で開くと以下のようになります。
これで、Remote SSH 接続ができました!
インスタンス内のディレクトリがエクスプローラで表示されており、ターミナルも利用出来るので便利ですね。
注意点
接続対象のインスタンスが ECS on EC2 の場合などで、定期的にインスタンス ID が変わる場合には、シェルスクリプトや SSH の config ファイルも併せて修正が必要になります。
私は仕方なく毎回書き換えていましたが、もっと良い方法もあるかもしれません。
まとめ
以上、VS Code を利用しながら、EC2 インスタンスコネクトを利用してセッションマネージャ経由で接続してみました。
EC2 インスタンス接続後にファイルをエディタで変更したり、ターミナルを複数開いて作業したい場合には便利な方法かなと思います。
どなたかのお役に立てば幸いです。それでは!
アノテーション株式会社について
アノテーション株式会社はクラスメソッドグループのオペレーション専門特化企業です。サポート・運用・開発保守・情シス・バックオフィスの専門チームが、最新 IT テクノロジー、高い技術力、蓄積されたノウハウをフル活用し、お客様の課題解決を行っています。当社は様々な職種でメンバーを募集しています。「オペレーション・エクセレンス」と「らしく働く、らしく生きる」を共に実現するカルチャー・しくみ・働き方にご興味がある方は、アノテーション株式会社 採用サイトをぜひご覧ください。