【AWS CDK】EC2 Instance Connect Endpoint 経由で EC2 インスタンスの Windows Server に RDP 接続してみた
はじめに
テントの中から失礼します。製造ビジネステクノロジー部の「てんとタカハシ」です!
EC2 Instance Connect Endpoint(EIC Endpoint)を使用することで、パブリック IP を持たないプライベートサブネット上の EC2 インスタンスに対してセキュアな接続が可能です。わざわざ踏み台サーバーを用意せずに、シンプルな構成で EC2 インスタンスに接続できる素敵な機能です。
この EIC Endpoint では、EC2 インスタンス上で稼働する Windows Server に対して RDP で接続することが可能です。
本記事では、EIC Endpoint を経由して EC2 インスタンスの Windows Server に RDP で接続する構成を AWS CDK で実装します。
開発環境
開発環境は下記の通りです。
% sw_vers
ProductName: macOS
ProductVersion: 14.6.1
BuildVersion: 23G93
% aws --version
aws-cli/2.22.7 Python/3.12.6 Darwin/23.6.0 exe/x86_64
% cdk --version
2.177.0 (build b396961)
実装のポイント
EIC Endpoint
EIC Endpoint を作成するための実装内容は下記の通りです。
- EIC Endpoint 用のセキュリティグループを作成(allowAllOutbound: false)
- EIC Endpoint → EC2 インスタンスの RDP 通信(TCP:3389)を許可するルールを追加
- EIC Endpoint から EC2 インスタンスへの Egress(送信)を許可
- EC2 インスタンスに対して EIC Endpoint からの Ingress(受信)を許可
- L1 コンストラクトで EIC Endpoint を作成(L2 は非対応のため)
// EIC Endpoint
const endpointSecurityGroup = new ec2.SecurityGroup(
this,
'Ec2InstanceConnectEndpointSecurityGroup',
{
vpc,
securityGroupName: `${projectName}-instance-connect-endpoint-security-group`,
allowAllOutbound: false,
}
);
// EIC Endpoint → EC2 インスタンスの RDP アクセスを許可(送信側)
endpointSecurityGroup.addEgressRule(
instanceSecurityGroup,
ec2.Port.tcp(3389)
);
// EIC Endpoint → EC2 インスタンスの RDP アクセスを許可(受信側)
instanceSecurityGroup.addIngressRule(
endpointSecurityGroup,
ec2.Port.tcp(3389)
);
new ec2.CfnInstanceConnectEndpoint(this, 'Ec2InstanceConnectEndpoint', {
subnetId: vpc.selectSubnets({ subnetGroupName: 'private' }).subnetIds[0],
securityGroupIds: [endpointSecurityGroup.securityGroupId],
});
EIC Endpoint を CDK で実装する内容は下記の記事を参考にさせていただきました。
AMI のバージョン固定
AMI のバージョンを固定化することで、デプロイ時に意図しない EC2 インスタンスの再作成を防ぎます。
const machineImage = new ec2.LookupMachineImage({
name: `${ec2.WindowsVersion.WINDOWS_SERVER_2019_JAPANESE_FULL_BASE}-*`,
filters: {
'image-type': ['machine'],
state: ['available'],
},
owners: ['amazon'],
windows: true,
});
// こちらだと AMI のバージョンが更新されるたびに EC2 インスタンスが再作成されてしまう
// const machineImage = new ec2.WindowsImage(
// ec2.WindowsVersion.WINDOWS_SERVER_2019_JAPANESE_FULL_BASE
// );
LookupMachineImage
で使用する AMI を指定すると cdk.context.json
に AMI のバージョンがキャッシュされます。
その結果、以降のデプロイでは同じ AMI の ID が使用され、意図しない差分の発生を防ぐことができます。
{
...
"ami:account=xxxxxxxxxxxx:filters.image-type.0=machine:filters.name.0=Windows_Server-2019-Japanese-Full-Base-*:filters.platform.0=windows:filters.state.0=available:owners.0=amazon:region=ap-northeast-1": "ami-028a21172be6f0723"
...
}
AMI のバージョン固定化は下記の記事を参考にさせていただきました。
ソースコード
VPC
下記の記事に記載している VPC の実装と同じです。
EC2
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as iam from 'aws-cdk-lib/aws-iam';
type Ec2StackProps = cdk.StackProps & {
projectName: string;
};
export class Ec2Stack extends cdk.Stack {
constructor(scope: Construct, id: string, props: Ec2StackProps) {
super(scope, id, props);
const { projectName } = props;
// VPC
const vpcName = `${projectName}-vpc`;
const vpc = ec2.Vpc.fromLookup(this, 'Vpc', {
vpcName,
});
// https://docs.aws.amazon.com/ja_jp/prescriptive-guidance/latest/optimize-costs-microsoft-workloads/right-size-selection.html#right-size-selection-next-steps
const instanceType = ec2.InstanceType.of(
ec2.InstanceClass.T3,
ec2.InstanceSize.MEDIUM
);
const machineImage = new ec2.LookupMachineImage({
name: `${ec2.WindowsVersion.WINDOWS_SERVER_2019_JAPANESE_FULL_BASE}-*`,
filters: {
'image-type': ['machine'],
state: ['available'],
},
owners: ['amazon'],
windows: true,
});
// こちらだと AMI のバージョンが更新されるたびに EC2 インスタンスが再作成されてしまう
// const machineImage = new ec2.WindowsImage(
// ec2.WindowsVersion.WINDOWS_SERVER_2019_JAPANESE_FULL_BASE
// );
const instanceSecurityGroup = new ec2.SecurityGroup(
this,
'Ec2InstanceSecurityGroup',
{
vpc: vpc,
securityGroupName: `${projectName}-instance-security-group`,
}
);
const instanceRole = new iam.Role(this, 'EC2InstanceRole', {
roleName: `${projectName}-instance-role`,
assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'),
});
const keyPairName = `${projectName}-ec2-key`;
const keyPair = new ec2.KeyPair(this, 'EC2KeyPair', {
keyPairName,
});
const volumeSize = 100;
const vpcSubnets = vpc.selectSubnets({ subnetGroupName: 'private' });
const blockDevices = [
{
// ルートボリューム上書き
deviceName: '/dev/sda1',
volume: ec2.BlockDeviceVolume.ebs(volumeSize, {
volumeType: ec2.EbsDeviceVolumeType.GP3,
}),
},
];
new ec2.Instance(this, 'EC2Instance', {
vpc,
vpcSubnets,
instanceName: `${projectName}-instance`,
instanceType,
machineImage,
securityGroup: instanceSecurityGroup,
role: instanceRole,
keyPair,
blockDevices,
ebsOptimized: true,
requireImdsv2: true,
});
// EIC Endpoint
const endpointSecurityGroup = new ec2.SecurityGroup(
this,
'Ec2InstanceConnectEndpointSecurityGroup',
{
vpc,
securityGroupName: `${projectName}-instance-connect-endpoint-security-group`,
allowAllOutbound: false,
}
);
// EIC Endpoint → EC2 インスタンスの RDP アクセスを許可(送信側)
endpointSecurityGroup.addEgressRule(
instanceSecurityGroup,
ec2.Port.tcp(3389)
);
// EIC Endpoint → EC2 インスタンスの RDP アクセスを許可(受信側)
instanceSecurityGroup.addIngressRule(
endpointSecurityGroup,
ec2.Port.tcp(3389)
);
new ec2.CfnInstanceConnectEndpoint(this, 'Ec2InstanceConnectEndpoint', {
subnetId: vpc.selectSubnets({ subnetGroupName: 'private' }).subnetIds[0],
securityGroupIds: [endpointSecurityGroup.securityGroupId],
});
}
}
RDP で接続する手順
RDP 接続用のパスワードを取得
CDK で EC2 インスタンスのキーペアを作成した場合、秘密鍵の内容は SSM パラメータストアで確認できます。秘密鍵の内容をファイルに保存してください。
対象の EC2 インスタンスを選択して「接続」に進みます。
「RDP クライアント」から「パスワードを取得」に進みます。
「プライベートキーファイルのアップロード」で秘密鍵をアップロードした後、「パスワードを復号化」します。
RDP 接続用のパスワードが表示されますので手元にコピーします。
WebSocket トンネルを作成
下記を実行して WebSocket トンネルを作成します。
% aws ec2-instance-connect open-tunnel \
--instance-id <対象の EC2 インスタンス ID> \
--remote-port 3389 \
--local-port 13389
Windows App をインストール
Microsoft が提供しているリモート接続アプリ「Windows App」を MacBook Pro にインストールします。このアプリは、従来の Microsoft Remote Desktop の後継にあたるもので、RDP 接続に対応しています。
Windows App から RDP 接続
Windows App の画面右上「+」から「Add PC」を選択します。
「PC name」に localhost:13389
を入力した後、「Add」します。
localhost:13389
を選択します。
下記を入力した後、「Continue」します。
- Username:
Administrator
- Password:
先ほどコピーしたパスワード
「Continue」します。
Windows Server への接続が完了します!
おわりに
本記事では、EC2 Instance Connect Endpoint(EIC Endpoint)を経由して EC2 インスタンスの Windows Server に RDP で接続する構成を AWS CDK で実装してみました。EIC Endpoint を使用することで、踏み台サーバーを用意せずにセキュアかつシンプルな構成を比較的簡単に構築できるのは非常に便利です。
個人的には、Windows Server 上で稼働しているシステムを AWS にマイグレーションするためのサービスや機能を検証する際にも活用しています。この環境で検証した内容を別の記事で公開できるように様々な検証を試していこうと考えています。
最後までお読みいただきありがとうございました!この記事が少しでも参考になれば幸いです。