SSM アクセスOK!踏み台 EC2 インスタンスを一括で作成してみた(CloudFormation 利用)
アノテーション テクニカルサポートの Shimizu です。
前回執筆した記事「バックエンド EC2 への直接アクセスを検証してみた」ではプライベートサブネットに配置された EC2 インスタンスに対して踏み台 EC2 を経由し、直接アクセスを検証する方法をご紹介しました。
しかしながら踏み台 インスタンスや SSM 接続に必要な VPC エンドポイントには課金が生じるため、検証のたびに毎回リソースを作成・削除するのが面倒という方もいるかと思います。
そこで今回は 踏み台 EC2 インスタンスと VPC エンドポイントを一括で作成し、検証が終わったらすぐに削除できる CloudFormation テンプレートを作成してみました!
CloudFormation テンプレートの内容
下記のテンプレートコードを丸ごとコピーしてテキストファイルに貼り付け、bastion-ec2.yml
などの適当なファイル名で保存して使用します。
テンプレートのコードはこちら
AWSTemplateFormatVersion: '2010-09-09'
Description: 'CloudFormation template to launch Bastion EC2 instance with SSM access and optional VPC endpoints'
Parameters:
# 踏み台インスタンスを起動する VPC ID とサブネット ID
VpcId:
Type: AWS::EC2::VPC::Id
Description: VPC where the Bastion instance will be launched
SubnetId:
Type: AWS::EC2::Subnet::Id
Description: Subnet where the Bastion instance will be launched
# 踏み台インスタンスのインスタンスタイプ
InstanceType:
Type: String
Default: t3.medium
Description: EC2 instance type
AllowedValues:
- t3.micro
- t3.small
- t3.medium
- t3.large
# 踏み台インスタンスの起動に使用する AMI ID が格納された SSM パラメータ名
# ※ 下記ドキュメントのパブリックパラメータから指定することも、独自に作成した SSM パラメータを指定することも可能
# https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/parameter-store-public-parameters-ami.html
AmiId:
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: /aws/service/ami-windows-latest/Windows_Server-2022-English-Full-Base
Description: SSM Parameter of AMI for Bastion instance (default - Windows Server 2022)
# 踏み台インスタンスに紐づける既存のキーペア名
KeyName:
Type: AWS::EC2::KeyPair::KeyName
Description: Name of an existing EC2 KeyPair to connect Bastion instance
Default: ""
# VPC エンドポイントの作成有無
# 対象サブネットにすでに SSM エンドポイントが存在する場合や、対象サブネットがパブリックの場合は No を選択する
CreateVpcEndpoints:
Type: String
Default: "Yes"
AllowedValues:
- "Yes"
- "No"
Description: Create VPC Endpoints for SSM access (for private subnets)
Conditions:
HasKeyName: !Not [!Equals [!Ref KeyName, ""]]
ShouldCreateVpcEndpoints: !Equals [!Ref CreateVpcEndpoints, "Yes"]
Resources:
# 踏み台インスタンスにアタッチする IAM ロール(SSM 接続に必要なポリシーのみ付与)
EC2SSMRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
Path: /
# Instance Profile for the EC2 instance
EC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Path: /
Roles:
- !Ref EC2SSMRole
# 踏み台インスタンスにアタッチするセキュリティグループ
# SSM 接続するのみであればインバウンドルールの許可は不要
BastionSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for Bation instance (No inbound access)
VpcId: !Ref VpcId
Tags:
- Key: Name
Value: bastion-server-sg
# 踏み台 EC2 インスタンス
BastionInstance:
Type: AWS::EC2::Instance
Properties:
InstanceType: !Ref InstanceType
ImageId: !Ref AmiId
SubnetId: !Ref SubnetId
SecurityGroupIds:
- !GetAtt BastionSecurityGroup.GroupId
IamInstanceProfile: !Ref EC2InstanceProfile
KeyName: !If [HasKeyName, !Ref KeyName, !Ref "AWS::NoValue"]
Tags:
- Key: Name
Value: Bastion Server
# VPC エンドポイントにアタッチするセキュリティグループ(踏み台インスタンスの SG からのみ接続を許可)
VpcEndpointSecurityGroup:
Type: AWS::EC2::SecurityGroup
Condition: ShouldCreateVpcEndpoints
Properties:
GroupDescription: Security group for VPC Endpoints
VpcId: !Ref VpcId
SecurityGroupIngress:
- Description: HTTPS from EC2 instance security group
IpProtocol: tcp
FromPort: 443
ToPort: 443
SourceSecurityGroupId: !GetAtt BastionSecurityGroup.GroupId
Tags:
- Key: Name
Value: vpc-endpoints-sg
# SSM 接続に必要な VPC エンドポイント(ssm/ssmmessages)
SSMEndpoint:
Type: AWS::EC2::VPCEndpoint
Condition: ShouldCreateVpcEndpoints
Properties:
VpcEndpointType: Interface
PrivateDnsEnabled: true
ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssm"
VpcId: !Ref VpcId
SubnetIds:
- !Ref SubnetId
SecurityGroupIds:
- !Ref VpcEndpointSecurityGroup
SSMMessagesEndpoint:
Type: AWS::EC2::VPCEndpoint
Condition: ShouldCreateVpcEndpoints
Properties:
VpcEndpointType: Interface
PrivateDnsEnabled: true
ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssmmessages"
VpcId: !Ref VpcId
SubnetIds:
- !Ref SubnetId
SecurityGroupIds:
- !Ref VpcEndpointSecurityGroup
Outputs:
InstanceId:
Description: ID of the EC2 instance
Value: !Ref BastionInstance
PrivateIP:
Description: Private IP address of the EC2 instance
Value: !GetAtt BastionInstance.PrivateIp
AMIId:
Description: AMI ID used for this instance
Value: !Ref AmiId
SSMConnectionCommand:
Description: Command to connect to the instance using SSM Session Manager
Value: !Sub "aws ssm start-session --target ${BastionInstance}"
SecurityGroupId:
Description: Security Group ID attached to the instance
Value: !GetAtt BastionSecurityGroup.GroupId
KeyPairUsed:
Description: Key pair used for this instance (if any)
Value: !If [HasKeyName, !Ref KeyName, "No key pair specified"]
PasswordRetrievalCommand:
Description: Command to retrieve the Windows password (if key pair was specified)
Value: !If
- HasKeyName
- !Sub "aws ec2 get-password-data --instance-id ${BastionInstance} --priv-launch-key path/to/your/${KeyName}.pem"
- "No key pair specified, password retrieval not available"
VpcEndpointsCreated:
Description: Whether VPC Endpoints were created
Value: !If [ShouldCreateVpcEndpoints, "Yes", "No"]
SSMEndpointId:
Description: SSM VPC Endpoint ID (if created)
Value: !If [ShouldCreateVpcEndpoints, !Ref SSMEndpoint, "Not created"]
SSMMessagesEndpointId:
Description: SSM Messages VPC Endpoint ID (if created)
Value: !If [ShouldCreateVpcEndpoints, !Ref SSMMessagesEndpoint, "Not created"]
このテンプレートで作成されるリソースは以下の通りです。
- EC2 インスタンス、アタッチする IAM ロールとセキュリティグループ
(SSM 接続に必要な権限のみ付与) - オプション:VPC エンドポイント(ssm,ssmmessages)、アタッチするセキュリティグループ(EC2 からの接続のみを許可)
やってみた
まず上記のテンプレートコードを .yml ファイルとしてPC上に保存します。あと EC2 に紐づけるキーペアが必要になるため、無い場合は作成しておきましょう。
CloudFormation のコンソール画面より「スタックの作成 > 新しいリソースを使用(標準)」を選択します。次の画面で「テンプレートファイルのアップロード」を選択し、先ほどローカルに保存した .yml ファイルを指定して次に進みます
次の画面で各パラメータを以下のように入力して、次の画面に進みます。
- スタック名
適当で構いません。 - AmiID
踏み台 EC2 の起動元となる AMI の ID が格納された SSM パラメータを指定します。
デフォルトでは Windows Server 2022 になっていますが、参考資料に記載された Amazon Linux 2023 に変更することも、自前で用意したカスタム AMI の ID を SSM パラメータに格納し、そのパラメータ名を指定することも可能です。 - CreateVpcEndpoints
SSM 接続に必要な VPC エンドポイント(ssm,ssmmessages)の作成有無を選択します。ターゲットの VPC にすでにこれらのエンドポイントがある場合や、パブリックサブネットに起動する場合は不要なのでNo
を選択します。 - InstanceType
接続するだけなら最小限のインスタンスタイプでOKですが、追加ソフトウェアのインストールが必要な場合などは適宜大きいサイズを選びましょう。 - KeyName
踏み台 EC2 に紐づける既存のキーペア名を指定します。 - SubnetId/VpcId
踏み台 EC2 を起動する VPC / サブネットを選択します。
次の画面では特に何も変更しなくてOKです。
踏み台 EC2 にアタッチされる IAM ロールも同時に作成されるため承認にチェックを入れて次に進み、入力した内容を確認して「送信」をクリックします。
一連のリソース作成が開始されるので、エラーが出ないか確認しつつ完了するまで待ちます。
(問題なければ数分で完了します)
スタックの作成が完了したら EC2 のコンソールに移動し「Bastion Server」という名前のインスタンスが起動していることを確認します。
画面上の「接続」をクリックし、次の画面で「Fleet Manager Remote Desktop」をクリックします。
次の画面の「ローカルマシンを参照して、キーペアファイルを選択します」でローカルPC上にある秘密鍵ファイル(.pem)を指定し「接続」をクリックします。
SSM 経由で踏み台 EC2 にリモートデスクトップ接続ができました!ここから他の EC2 に対してアクセス検証などが行えます。
検証が終わったら CloudFormation スタック一覧画面で今回作成したスタックを選択し「削除」ボタンをクリックするだけで、EC2 をはじめとして IAM ロールやセキュリティグループ、VPC エンドポイントなど、関連するリソースを一括で削除できます。
おわりに
いかがでしたでしょうか。
検証で踏み台 EC2 インスタンスや SSM エンドポイントを利用する機会が多いものの、毎回削除して課金を抑えたい方には便利なテンプレートだと思います。ぜひ活用いただけると幸いです!
参考資料
- 参考1: ELB ヘルスチェック失敗時の切り分け!バックエンド EC2 への直接アクセスを検証してみた | DevelopersIO
- 参考2: Parameter Store で AMI パブリックパラメータを呼び出す - AWS Systems Manager
アノテーション株式会社について
アノテーション株式会社はクラスメソッドグループのオペレーション専門特化企業です。サポート・運用・開発保守・情シス・バックオフィスの専門チームが、最新 IT テクノロジー、高い技術力、蓄積されたノウハウをフル活用し、お客様の課題解決を行っています。当社は様々な職種でメンバーを募集しています。「オペレーション・エクセレンス」と「らしく働く、らしく生きる」を共に実現するカルチャー・しくみ・働き方にご興味がある方は、アノテーション株式会社 採用サイトをぜひご覧ください。