使い捨て環境をサクッと作りたくてバッチを適当に書いた。

実行するとSSM経由で接続できるEC2を起動し、SSHの設定と共にKeyPairを取得して配置してVSCodeをリモートモードで起動する。
起動と同時に以下のようなバッチも生成し簡単に削除できるようにした。

Delete-{インスタンス名}.bat
@REM SSHConfの削除
@RD /S /Q "C:\Users\szk/.ssh/conf.d/ephemeral/amz-ec2-20220930072729"
@REM リソースの削除
aws cloudformation delete-stack --stack-name "amz-ec2-20220930072729"
@DEL %0

準備

VSCodeからSSH経由接続するため、バッチ実行時に以下のディレクトリにSSH設定の設定のプライベートキーを出力する。

  • ~/.ssh/conf.d/ephemeral/${インスタンス名}

この設定を有効化するためには予め ~/.ssh/confg に以下の設定を追加してインポートできるようにする。

(例)~/.ssh/config
Include conf.d/ephemeral/*/*.conf

内容

  1. StartSandboxAmzLinuxEc2.bat: エントリーポイントのバッチ

  2. amz-ec2.yml: EC2を起動するためのCloudFormation template

  3. scripts/StartSandboxAmzLinuxEc2.ps1: 処理を記述したスクリプト

ディレクトリ構成
./
├── StartSandboxAmzLinuxEc2.bat
├── amz-ec2.yml
└── scripts
    └── StartSandboxAmzLinuxEc2.ps1
1. StartSandboxAmzLinuxEc2.bat
powershell -ExecutionPolicy Bypass -command .\scripts\StartSandboxAmzLinuxEc2.ps1
pause
2. amz-ec2.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Create EC2 Instance
Parameters:
  latestAmazonLinux2AmiId:
    Type : AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
  instanceType:
    Type : String
    Default: t3.large
Resources:
  Ec2Role:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement: 
          - 
            Effect: Allow
            Principal: 
              Service: 
                - ec2.amazonaws.com
            Action: 
              - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
  Ec2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: /
      Roles:
      - !Ref Ec2Role
  Ec2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: EC2SecurityGroup
      # VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-Ec2SecurityGroup
  Ec2SecurityGroupEgress:
    Type: AWS::EC2::SecurityGroupEgress
    Properties:
      IpProtocol: -1
      CidrIp: 0.0.0.0/0
      GroupId: !GetAtt Ec2SecurityGroup.GroupId
  Ec2SecurityGroupIngress:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      IpProtocol: -1
      CidrIp: 127.0.0.1/32
      GroupId: !GetAtt Ec2SecurityGroup.GroupId
  Ec2KeyPair:
    Type: AWS::EC2::KeyPair
    Properties:
      KeyName: !Sub ${AWS::StackName}-KeyPair
  Ec2LaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateName: !Sub ${AWS::StackName}-LaunchTemplate
      LaunchTemplateData:
        UserData:
          Fn::Base64: |-
            #!/bin/bash
            yum update -y
            amazon-linux-extras install docker -y
            systemctl enable docker
            systemctl start docker
            usermod -aG docker ec2-user
            curl -L --fail https://github.com/docker/compose/releases/download/1.29.2/run.sh -o /usr/local/bin/docker-compose
            chmod +x /usr/local/bin/docker-compose
  Ec2:
    Type: AWS::EC2::Instance
    Properties: 
      ImageId: !Ref latestAmazonLinux2AmiId
      InstanceType: !Ref instanceType
      KeyName: !Ref Ec2KeyPair
      SecurityGroupIds:
        - !Ref Ec2SecurityGroup
      IamInstanceProfile:
        !Ref Ec2InstanceProfile
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}
      LaunchTemplate:
        LaunchTemplateId: !Ref Ec2LaunchTemplate
        Version: !GetAtt Ec2LaunchTemplate.LatestVersionNumber
Outputs:
  Ec2InstanceId:
    Value: !Ref Ec2
    Export:
      Name: !Sub ${AWS::StackName}-Ec2InstanceId
  KeyPairId:
    Value: !GetAtt Ec2KeyPair.KeyPairId
    Export:
      Name: !Sub ${AWS::StackName}-KeyPairId
  KeyPairPath:
    Value: !Sub
      - /ec2/keypair/${KeyPairId}
      - KeyPairId: !GetAtt Ec2KeyPair.KeyPairId
    Export:
      Name: !Sub ${AWS::StackName}-KeyPairPath
3.scripts/StartSandboxAmzLinuxEc2.ps1
$stackName="amz-ec2-$(Get-Date -Format yyyyMMddHHmmss)"
$instanceName="${stackName}"
$sshConfigDirPath="${Home}/.ssh/conf.d/ephemeral/${stackName}"
$sshConfigFilePath="${sshConfigDirPath}/${instanceName}.conf"
$keyFileName="${instanceName}.pem"
$keyFilePath="${sshConfigDirPath}/${keyFileName}"
$remoteHomeDirPath="/home/ec2-user"
$deleteBatFilePath="Delete-${stackName}.bat"

echo "###########################################"
echo "# StackName: ${stackName}"
echo "# InstanceName: ${instanceName}"
echo "# KeyFileName: ${keyFileName}"
echo "# KeyFilePath: ${keyFilePath}"
echo "# SshConfigFilePath: ${sshConfigFilePath}"
echo "###########################################"

## SSHConfigディレクトリの作成
mkdir "${sshConfigDirPath}" > $null

## リソースの作成
aws cloudformation deploy --template-file ./amz-ec2.yml --stack-name "${stackName}" --capabilities CAPABILITY_NAMED_IAM
## リソース作成待ち
aws cloudformation wait stack-create-complete --stack-name "${stackName}"
## インスタンスIDの取得
$instanceId = (aws cloudformation describe-stacks --stack-name "${stackName}" --query "Stacks[0].Outputs[?OutputKey==``Ec2InstanceId``].OutputValue" --output text).trim()
echo "instanceId:${instanceId}"
## EC2起動まち
aws ec2 wait instance-status-ok --include-all-instances --instance-ids "${instanceId}"
## キーペア取得
### キーペア名を取得
$keyPairName = (aws cloudformation describe-stacks --stack-name "${stackName}" --query "Stacks[0].Outputs[?OutputKey==``KeyPairPath``].OutputValue" --output text).trim()
echo "keyPairName:${keyPairName}"
### キーペアを取得
aws ssm get-parameter --name "${keyPairName}" --with-decryption --query "Parameter.Value" --output text | Set-Content -Path "${keyFilePath}"

## SSH設定の生成
$sshConfig = @"
Host ${instanceName}
    User ec2-user
    Port 22
    ProxyCommand powershell.exe aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters portNumber=%p
    HostName ${instanceId}
    IdentityFile ${keyFilePath}
"@
Set-Content -Path $sshConfigFilePath -Value $sshConfig

## VSCodeの起動
code --remote "ssh-remote+${instanceName}" "${remoteHomeDirPath}"

## 削除バッチの生成
$deleteBat = @"
@REM SSHConfの削除
@RD /S /Q "${sshConfigDirPath}"
@REM リソースの削除
aws cloudformation delete-stack --stack-name "${stackName}"
@DEL %0
"@
Set-Content -Path $deleteBatFilePath -Value $deleteBat

実行

作成

  • StartSandboxAmzLinuxEc2.bat をクリック

削除

  • Delete-${インスタンス名}.bat をクリック