AWS Tools for PowerShellを使ってEC2インスタンスを作成する

しばたです。

EC2インスタンスを作成する場合、環境をゼロから新規構築したりある程度まとまった規模で管理する場合であればCloudFormationTerraformといった構成管理ツールを使うかと思いますが、一時的な検証環境であったり既存の環境に追加でインスタンスを増やす場合はAWS CLIやPowerShellを使ってコマンドやスクリプトで作成することも多いかと思います。

本記事では既存の環境に新たに1台のEC2インスタンスを作成するシナリオでAWS Tools for PowerShellの使い方を紹介します。

前提条件

VPCやサブネット、セキュリティグループの設定は一通り設定済みの環境を想定しています。
EC2キーペアを新しく発行し、新規のEC2インスタンスを作成する流れを紹介します。

AWS Tools for PowerShellのインストールと初期設定

本記事で使うPowerShellは最新のPowerShell Core 6.1.3とします。
これから紹介する手順は古いPowerShellでも動作しますが、最新のPowerShell CoreであればWindows以外の環境からも試すことができより多くの人にリーチできるので最新バージョンを選択します。

PowerShell Coreのインストール手順はGitHubのドキュメントか私が使ってるスクリプトを参考にしてください。

AWS Tools for PowerShellPowerShell Galleryからインストールします。
(本記事公開時点のバージョンはVer.3.3.462.0)

#Requires -Version 6.0

# AWS Tools for PowerShell インストール
Install-Module -Name AWSPowerShell.NetCore -Scope CurrentUser

認証の設定などは私の個人ブログを参照してください。

以降の手順ではSet-DefaultAWSRegionSet-AWSCredentialを呼び出し既定のリージョンとプロファイルが指定済みの前提とします。

#Requires -Version 6.0

# モジュールをインポート
Import-Module AWSPowerShell.NetCore

# 既定のリージョンおよびプロファイルの指定は環境に合わせて行ってください
Set-DefaultAWSRegion -Region ap-northeast-1
Set-AWSCredential -ProfileName test-profile

以降で紹介するコマンドではリージョンやプロファイル指定が省略されています。
環境に応じて適宜リージョンやプロファイルを含む認証情報の指定を行ってください。

AMIの検索

作成するインスタンスのAMIはGet-EC2Imageコマンドレットで検索することができます。
このコマンドレットを引数なしで実行すると全種類、全バージョンのAMIを検索して使い物になりませんのでFilterパラメーターを使い検索条件を指定してやるのが良いです。

このFilterパラメーターはAmazon.EC2.Model.Filter[]と独自の型の配列を受け取るのですが、ハッシュテーブルで代用できるため以下の例の様に使う方がPowerShellとして自然ですし記述も楽になるかと思います。
指定可能なパラメーターについてはリファレンスを参照してください。

本記事ではAmazonが提供する日本語版Windows Server 2019を検索する例を紹介します。
短いコードですので記載してるコメントだけで十分意味は通るとかと思います。

# AMIの検索 : Filterパラメーターを使わないとすごく遅いので注意
#   日本語版Windows Server 2019を検索する場合
#   イメージ情報をすべて出力すると冗長なので ImageId, Name, Description のみ表示する様にしています
$Filter = (
    @{ Name = 'platform'; Value = 'windows'},
    @{ Name = 'name'; Value = 'Windows_Server-2019-Japanese*Base*'}
)
Get-EC2Image -Owner amazon -Filter $Filter |
  Sort-Object CreationDate -Descending | Select-Object ImageId, Name, Description | Format-List

また、とりあえずWindows AMIが欲しい場合であればGet-EC2ImageByNameコマンドレットも使えます。
(最新バージョンの英語版Windowsのみが検索対象となります)

# 引数なしですべての名前をリストアップ
Get-EC2ImageByName

# 引数を付けると最新のAMIを取得
Get-EC2ImageByName -Name WINDOWS_2016_BASE | Select-Object ImageId, Name, Description | Format-List

EC2キーペアの作成

EC2キーペアの作成はNew-EC2KeyPairコマンドレットで行えます。
何も考えずに実行すると「フィンガープリントと鍵情報がコンソールに出力されて終わり」となってしまうので以下の様にキー情報を変数に格納してファイル出力してやると良いでしょう。

# キーペアを作成
# ※結果を変数に入れないと二度と取得できなくなり、鍵を作り直すことになるので注意
$keyName = 'your-key'
$keyPair = New-EC2KeyPair -KeyName $keyName

# フィンガープリントを表示
Write-Output ("Fingerprint : {0}" -f $keyPair.KeyFingerprint)
# 秘密鍵を保存
$keyPair.KeyMaterial | Out-File (".\{0}.pem" -f $keyName) -Encoding ascii

EC2インスタンスの作成 (Windows)

ここから本題のEC2インスタンスの作成について説明します。

インスタンスの作成はNew-EC2Instanceコマンドレットで行えます。
このコマンドレットはAMIイメージIDを指定するパターンと起動テンプレートを指定する2パターン存在するのですが今回はAMIイメージを指定するパターンを紹介します。
また、EC2インスタンスを作成するだけあって指定可能なパラメーターが多いため、個々のパラメーターについてはリファレンスで確認してください。

本記事では少し実践的な例を紹介します。

  • 現時点で最新の日本語版 Windows Server 2019 を作成
  • インストールするサブネットはNameタグにyour-subnetが設定されているものを選択
  • 設定するセキュリティグループはNameタグにyour-security-groupが設定されているものを選択
  • 30GiBのCドライブ以外に10GiBの追加ドライブも同時作成
  • ユーザーデータで追加ドライブの初期化処理を記述
    • おまけで柴田個人作成のPowerShell Coreインストールスクリプトも追記
  • インスタンス追加後にNameタグにyour-windows-server2019を設定
  • インスタンス追加後に削除保護を設定

上記の条件でインスタンスを作成するスクリプトが以下になります。
コメントである程度説明してますが補足は後述します。

#Requires -Version 6.0

# EC2インスタンスを作成
# ※パラメーターが多いのでスプラッティングを使用
$params = @{
    KeyName = 'your-key'              # EC2キーペア
    ImageId = 'ami-0080a567a9989e1d5' # Windows_Server-2019-Japanese-Full-Base-2019.02.13
    InstanceType = 't2.medium'        # 無償枠から外れるが Windows ServerならCPU2コアはないと厳しい
    # 基本的にMinCountとMaxCountは同じで良い 
    MinCount = 1 
    MaxCount = 1
    # NameタグからサブネットIDを取得して指定
    SubnetId = (Get-EC2Subnet -Filter @{Name = 'tag:Name'; Value = 'your-subnet'}).SubnetId
    # NameタグからセキュリティグループIDを取得して指定
    SecurityGroupId = (Get-EC2SecurityGroup | Where-Object {$_.GroupName -eq 'your-security-group'}).GroupId
    EbsOptimized = $false
    #
    # デバイス指定は Amazon.EC2.Model.EbsBlockDevice 型のオブジェクトを直接使う必要があり、
    # PowerShellらしい記述ができないのが少し残念である
    #
    BlockDeviceMapping = &{
        # EBS1 : ルートデバイス(/dev/sda1)
        $bd1 = [Amazon.EC2.Model.EbsBlockDevice]::new()
        $bd1.VolumeSize = 30
        $bd1.VolumeType = [Amazon.EC2.VolumeType]::Gp2
        $bd1.DeleteOnTermination = $true
        $bdm1 = [Amazon.EC2.Model.BlockDeviceMapping]::new()
        $bdm1.DeviceName = '/dev/sda1'
        $bdm1.Ebs = $bd1
        # EBS2 : 追加ディスク (xvdf)
        $bd2 = [Amazon.EC2.Model.EbsBlockDevice]::new()
        $bd2.VolumeSize = 10
        $bd2.VolumeType = [Amazon.EC2.VolumeType]::Gp2
        $bd2.DeleteOnTermination = $true
        $bdm2 = [Amazon.EC2.Model.BlockDeviceMapping]::new()
        $bdm2.DeviceName = 'xvdf'
        $bdm2.Ebs = $bd2
        return @($bdm1, $bdm2)
    }
    # ユーザーデータの設定
    EncodeUserData = $true
    UserData = 
@'
<powershell>
# Initialize disks
& (Join-Path $env:ProgramData 'Amazon\EC2-Windows\Launch\Scripts\InitializeDisks.ps1')

# Install the latest PowerShell Core
[Net.ServicePointManager]::SecurityProtocol=[Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12;
iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/stknohg/PSCoreUpdate/master/FirstTimeInstaller/Install-LatestPowerShell.ps1'))
</powershell>
'@
}
# インスタンス作成
$reservation = New-EC2Instance @params
$reservation.Instances | ForEach-Object {
    # Nameタグを追加
    New-EC2Tag -Resource $_.InstanceId -Tag @{Key = 'Name'; Value = 'your-windows-server2019'}
    # 削除保護の追加
    Edit-EC2InstanceAttribute -InstanceId $_.InstanceId -DisableApiTermination $true
}

MinCount、MaxCount

New-EC2Instanceでは一回の実行で複数インスタンスを同時に作成することができ、コマンドレットの戻り値もインスタンスそのものではなくReservationを返します。
一応最小作成数がMinCount、最大作成数がMaxCountで、基本的にMaxCountの作成を試みるのですがリソース制限に引っかかった際の挙動についてはよくわかりません。(別途検証したいと思います)
AWS CLIではこの様なパラメーターは無く単純に--count引数で作成数を指定するだけです。
このパラメーターについては両方同じ数にしておくのが無難だと思います。

サブネット、セキュリティグループの指定

SubnetIdパラメーターでインスタンスを配置するサブネットを、SecurityGroupIdパラメーターで関連付けるセキュリティグループを指定します。
どちらもID指定ですが、一般的な環境であればNameタグを使って名前付けしてると思いますので、それぞれGet-EC2SubnetGet-EC2SecurityGroupコマンドレットを使い、名前からIDを取得する様にしています。

BlockDeviceMapping

ルートデバイスおよび追加EBSボリュームの設定はBlockDeviceMappingパラメーターで指定します。
このパラメーターにはAWS SDK for .NETのAmazon.EC2.Model.EbsBlockDevice型をそのまま指定します。
この型では追加するディスクを表すAmazon.EC2.Model.EbsBlockDevice型のオブジェクトをEbsプロパティに、デバイス名をDeviceNameプロパティに設定してやります。

この記述は生のSDKそのままであまりPowerShellらしくないため若干わかりにくいです。
本記事ではやりませんが簡単な関数を作ってこの記述をラップしてやるとよりPowerShellらしくなると思います。

ユーザーデータ

ユーザーデータはUserDataプロパティに処理を記述します。
デフォルトではBASE64エンコードした後の値を入れるのですが、EncodeUserDataパラメーターを指定することでエンコード前の値を設定でき、より直感的なコードになります。

ディスクの初期化についてはこちらの記事をご覧ください。

おまけで私個人作成のPowerShell Coreのインストールスクリプトを記述してます。
これでインスタンス作成と同時にPowerShell Coreが利用可能になります。

タグ付け

【2019.03.08修正】
EC2インスタンスのタグを作成と同時に行うことはできません。
New-EC2Instanceの戻り値(Reservation)から作成したインスタンスを取得し、インスタンスを更新する形を採る必要があります。

TagSpecificationパラメーターを指定するとインスタンスの作成と同時にタグ付けすることができます。(パラメーターを見逃してました...すいません)
作成するインスタンスが1つだけの場合であればこのパラメーターを使う方が楽でしょう。
同時に複数インスタンス作成する場合はループ内でタグを更新する方がより柔軟に処理できると思います。
【修正ここまで】

前述した様にNew-EC2Instanceでは複数のインスタンスを同時に作成できます。
このため戻り値(Reservation)のInstancesプロパティに対してForEach-Objectでループ処理をしてやる必要があります。

New-EC2Tagで作成したインスタンスに対してタグ付けをしてやります。
このコマンドレットはEC2インスタンス以外のリソースでも使えるためパラメーター名がResourceとなっています。

削除保護の指定

Edit-EC2InstanceAttributeコマンドレットでEC2インスタンスの属性変更を行うことができます。
DisableApiTerminationパラメーターで削除保護の有効・無効を切り替えることができます。
他にも様々な属性を変更できますので詳細はリファレンスを参照してください。

EC2インスタンスの作成 (Linux)

続けてLinuxインスタンスの作成スクリプトを紹介します。
Windowsの場合と概ね同じ条件のインスタンスを作成します。

  • 現時点で最新のAmazon Linux 2を作成
  • インストールするサブネットはNameタグにyour-subnetが設定されているものを選択
  • 設定するセキュリティグループはNameタグにyour-security-groupが設定されているものを選択
  • 10GiBのルートディスク以外に10GiBの追加ディスクを/dataにマウント
  • ユーザーデータで追加ディスクのマウント処理を追加
    • おまけで柴田個人作成のPowerShell Coreインストールスクリプトも追記
  • インスタンス追加後にNameタグにyour-amazonlinux2を設定
  • インスタンス追加後に削除保護を設定

スクリプトは以下。
Windowsの場合とほとんど変わらないので同様の部分の補足は省略します。

#Requires -Version 6.0

# EC2インスタンスを作成
# ※パラメーターが多いのでスプラッティングを使用
$params = @{
    KeyName = 'your-key'              # EC2キーペア
    ImageId = 'ami-097473abce069b8e9' # amzn2-ami-hvm-2.0.20190228-x86_64-gp2
    InstanceType = 't1.micro'         # 無償枠
    # 基本的にMinCountとMaxCountは同じで良い 
    MinCount = 1
    MaxCount = 1
    # NameタグからサブネットIDを取得して指定
    SubnetId = (Get-EC2Subnet -Filter @{Name = 'tag:Name'; Value = 'your-subnet'}).SubnetId
    # NameタグからセキュリティグループIDを取得して指定
    SecurityGroupId = (Get-EC2SecurityGroup | Where-Object {$_.GroupName -eq 'your-security-group'}).GroupId
    EbsOptimized = $false
    #
    # デバイス指定は Amazon.EC2.Model.EbsBlockDevice 型のオブジェクトを直接使う必要があり、
    # PowerShellらしい記述ができないのが少し残念である
    #
    BlockDeviceMapping = &{
        # EBS1
        $bd1 = [Amazon.EC2.Model.EbsBlockDevice]::new()
        $bd1.VolumeSize = 10
        $bd1.VolumeType = [Amazon.EC2.VolumeType]::Gp2
        $bd1.DeleteOnTermination = $true
        $bdm1 = [Amazon.EC2.Model.BlockDeviceMapping]::new()
        $bdm1.DeviceName = '/dev/xvda'
        $bdm1.Ebs = $bd1
        # EBS2
        $bd2 = [Amazon.EC2.Model.EbsBlockDevice]::new()
        $bd2.VolumeSize = 10
        $bd2.VolumeType = [Amazon.EC2.VolumeType]::Gp2
        $bd2.DeleteOnTermination = $true
        $bdm2 = [Amazon.EC2.Model.BlockDeviceMapping]::new()
        $bdm2.DeviceName = '/dev/xvdf'
        $bdm2.Ebs = $bd2
        return @($bdm1, $bdm2)
    }
    # ユーザーデータの設定
    EncodeUserData = $true
    UserData = 
@'
#!/bin/bash
sudo yum update -y

# mkfs
sudo mkfs -t xfs /dev/xvdf
sudo mkdir /data
sudo mount /dev/xvdf /data
# set fstab
sudo cp /etc/fstab /etc/fstab.orig
echo "UUID=$(sudo blkid | grep '/dev/xvdf' | sed -n 's/.*UUID=\"\([^\"]*\)\".*/\1/p')   /data    xfs  defaults,nofail    1   2" | sudo tee -a /etc/fstab

# install PowerShell Core
curl https://packages.microsoft.com/config/rhel/7/prod.repo | sudo tee /etc/yum.repos.d/microsoft.repo
sudo yum install -y powershell
'@
}
# インスタンス作成
$reservation = New-EC2Instance @params
$reservation.Instances | ForEach-Object {
    # Nameタグを追加
    New-EC2Tag -Resource $_.InstanceId -Tag @{Key = 'Name'; Value = 'your-amazonlinux2'}
    # 削除保護の追加
    Edit-EC2InstanceAttribute -InstanceId $_.InstanceId -DisableApiTermination $true
}

ユーザーデータ

ユーザーデータに記述している追加ディスクのマウント手順はAWS公式ドキュメントの手順をベースにしています。

mkfsでファイルシステムを作ってマウント、fstabの記述を追加して永続化、とLinuxでの一般的な設定方法かと思います。
こちらもおまけでPowerShell Coreのインストールをしています。

最後に

ちょっと長くなりましたがAWS Tools for PowerShellを使ってEC2インスタンスを作成するスクリプトを紹介しました。
本記事で紹介したようなスクリプトを手元に持っておくとちょっとした環境を作る時に非常に役に立ちますので、みなさんも自分の環境に合わせたスクリプトを作ってみてください。