この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは。サービスグループの武田です。
Webアプリケーションを作成していると、さまざまな環境での動作確認が必要になるケースがあります。近年はクロスブラウザ対策などで比較的互換性は保てますが100%ではありません。最近、Windowsのブラウザで動作確認ができる環境を用意する機会がありました。
私は普段の業務ではMacを使用しており、Windowsの環境がないため、EC2でその環境を用意することにしました。その作業の一環として、設定済みのWindows Server 2016インスタンスをAWS CloudFormationで立ち上げるテンプレートを作ってみました。これを使用することで、必要なときに同じ環境を簡単に用意できるようになりました。
なお、AWSで提供しているWindows環境としてはAmazon WorkSpacesもあります。今回は「使用頻度が不定期かつ短時間」ということからEC2を利用することにしました。
環境
次の環境で動作確認をしています。
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.13.6
BuildVersion: 17G65
$ aws --version
aws-cli/1.15.33 Python/3.6.5 Darwin/17.7.0 botocore/1.10.33
$ jq --version
jq-1.5
また、EC2インスタンスに設定するキーペアは作成済みとします。
CloudFormationを利用するメリット
検証用のインスタンスですが、毎回削除ではなく、Stop/Startで使い続けることも考えられますね。私はCloudFormationを利用して毎回新規作成することで次のようなメリットがあると考えています。
- EBS費用の節約
- インスタンスを停止していてもEBSの料金はかかります。今回使用したAMIはデフォルトで30GBのストレージを使います。そのため何もしてなくても$3.6/月(約400円)の費用が発生します。使い終わったらスタック削除をすれば費用を抑えられます
- 最新のパッチが当たった状態で使える
- AWSが提供するAMIは定期的に更新されています。毎回新規に作り直すことで常に最新のAMIを使用できます
- おかしくなったら作り直せる
- 作り直せるようにしておくことで、何かトラブルがあっても簡単に初期状態に戻せる安心感があります。またバグの再現性にも活用できそうです
また言ってしまえばEC2インスタンスを起動するだけですので、EC2の起動テンプレートを利用する方法も考えられそうです。それについては次のような差異があると考えられます。
- バージョン管理可能
- 起動テンプレートはバージョン管理ができるため、複数のバージョンを用意しておくことでインスタンスの構成を切り替えるのが容易です
- 起動テンプレートはパラメータストアに対応していない
- 最新のAMI IDの取得に、CloudFormationではパラメータで対応できますが、起動テンプレートではインスタンス起動時に明示的に渡す必要があります
- セキュリティグループは事前に作成しておく
- CloudFormationではその場で作れますが、起動テンプレートでは事前に作成しておいたものをアタッチします
それぞれメリットデメリットあると思いますので、環境に合わせてより適したものを使ってください。
作成したCloudFormationテンプレート
それでは今回作成したテンプレートです。ファイル名はwindows-check-server.template.yml
としました。再利用はご自由にどうぞ。
windows-check-server.template.yml
---
AWSTemplateFormatVersion: '2010-09-09'
Description: This template create a EC2 instance.
Parameters:
KeyName:
Type: AWS::EC2::KeyPair::KeyName
ConstraintDescription: must be the name of an existing EC2 KeyPair.
InstanceType:
Type: String
Default: t3.small
AllowedValues:
- t3.micro
- t3.small
- t3.midium
ConstraintDescription: must be a valid EC2 instance type.
InstanceImageId:
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: /aws/service/ami-windows-latest/Windows_Server-2016-Japanese-Full-Base
SourceCidrForRDP:
Type: String
MinLength: '9'
MaxLength: '18'
AllowedPattern: '^([0-9]+\.){3}[0-9]+\/[0-9]+$'
TagName:
Type: String
Resources:
SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Enable RDP
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 3389
ToPort: 3389
CidrIp:
!Ref SourceCidrForRDP
WindowsServer:
Type: AWS::EC2::Instance
Metadata:
AWS::CloudFormation::Init:
config:
files:
c:\cfn\cfn-hup.conf:
content: !Sub |
[main]
stack=${AWS::StackId}
region=${AWS::Region}
c:\cfn\hooks.d\cfn-auto-reloader.conf:
content: !Sub |
[cfn-auto-reloader-hook]
triggers=post.update
path=Resources.WindowsServer.Metadata.AWS::CloudFormation::Init
action=cfn-init.exe -v --stack ${AWS::StackName} --resource WindowsServer --region ${AWS::Region}
c:\cfn\scripts\Setup-config.ps1:
content: |
# set JST TimeZone
tzutil /s "Tokyo Standard Time"
Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\TimeZoneInformation" -Name "RealTimeIsUniversal" -Value 1
# disabled firewall
Get-NetFirewallProfile | Set-NetFirewallProfile -Enabled false
# show fileext and hidden file
Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" -Name "HideFileExt" -Value 0
Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" -Name "Hidden" -Value 1
# set high performance
powercfg.exe -SETACTIVE 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c
# disabled enhanced security
# https://stackoverflow.com/questions/44643997/aws-windows-2012-r2-turning-off-ie-enhanced-security-configuration
$admin = "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}"
$user = "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}"
Set-ItemProperty -Path $admin -Name "IsInstalled" -Value 0 -Force
Set-ItemProperty -Path $user -Name "IsInstalled" -Value 0 -Force
Remove-ItemProperty -Path $admin -Name "IsInstalled" -Force
Remove-ItemProperty -Path $user -Name "IsInstalled" -Force
c:\cfn\scripts\Install-choco-packages.ps1:
content: |
# install package manager and packages
# suppressed warning. UnicodeEncodeError fails because the output contains Japanese.
$WarningPreference = "SilentlyContinue"
iwr https://chocolatey.org/install.ps1 -UseBasicParsing | iex
$WarningPreference = "Continue"
choco install -y notepadplusplus.install googlechrome firefox | Out-Null
commands:
1-setup-config:
command: 'powershell.exe -File c:\cfn\scripts\Setup-config.ps1'
waitAfterCompletion: '0'
2-install-choco-packages:
command: 'powershell.exe -File c:\cfn\scripts\Install-choco-packages.ps1 -ExecutionPolicy Bypass'
waitAfterCompletion: '0'
services:
windows:
cfn-hup:
enabled: 'true'
ensureRunning: 'true'
files:
- c:\cfn\cfn-hup.conf
- c:\cfn\hooks.d\cfn-auto-reloader.conf
Properties:
InstanceType: !Ref InstanceType
ImageId: !Ref InstanceImageId
SecurityGroupIds:
- !Ref SecurityGroup
KeyName: !Ref KeyName
Tags:
- Key: Name
Value: !Ref TagName
UserData:
Fn::Base64: !Sub |
<powershell>
cfn-init.exe -v --stack ${AWS::StackName} --resource WindowsServer --region ${AWS::Region}
cfn-signal.exe -e $lastexitcode --stack ${AWS::StackName} --resource WindowsServer --region ${AWS::Region}
</powershell>
CreationPolicy:
ResourceSignal:
Timeout: PT15M
Outputs:
WindowsServerHostname:
Value: !GetAtt WindowsServer.PublicDnsName
Description: WindowsServer Hostname.
いくつかポイントを解説します。
16-18行目はパラメータとして、「起動するインスタンスのイメージIDを格納しているAWS Systems Managerパラメータストアのキー名」を受け取ります。詳しくは中山のエントリを参照してください。社内で共有してもらって知りました。とても便利ですね!
54行目からはc:\cfn\scripts\Setup-config.ps1
を作成しています。このスクリプトでは次の設定を行っています。特に 「IE セキュリティ強化の構成」を無効化 はかなりはまったところで、試行錯誤の末やっと想定した挙動になりました。
- タイムゾーンをUTCからJSTに変更
- 「Windows ファイアウォール」を無効化
- ファイルの拡張子を表示
- 隠しファイルを表示
- 電源オプションを「高パフォーマンス」に変更
- 「IE セキュリティ強化の構成」を無効化
79行目からはc:\cfn\scripts\Install-choco-packages.ps1
を作成しています。このスクリプトでは次のソフトウェアをインストールしています。Chocolateyのインストールでは警告メッセージの抑制が必要で、これをしないとCloudFormationの初期化処理に失敗します。
- Chocolatey
- Notepad++
- Google Chrome
- Firefox
スタック作成スクリプト
次に、先ほど作成したCloudFormationテンプレートからスタックを作成するスクリプトです。ファイル名はcreate-stack-windows-check-server.sh
としました。4行目の$your_key_pair_name
は使用するキーペア名に置き換えてください。また5行目は使用するキーファイルのパスに置き換えてください。
create-stack-windows-check-server.sh
#!/bin/bash
stack_name=WindowsCheckServer
key_name=$your_key_pair_name
key_path="/path/to/${key_name}.pem"
aws cloudformation create-stack \
--template-body file://windows-check-server.template.yml \
--stack-name "${stack_name}" \
--parameters \
ParameterKey=KeyName,ParameterValue="${key_name}" \
ParameterKey=SourceCidrForRDP,ParameterValue="$(curl -s ifconfig.io)/32" \
ParameterKey=TagName,ParameterValue="${stack_name}"
aws cloudformation wait stack-create-complete --stack-name "${stack_name}"
hostname=$(aws cloudformation describe-stacks \
--stack-name "${stack_name}" \
| jq -r '.Stacks[].Outputs[] | select(.OutputKey=="WindowsServerHostname") | .OutputValue')
instance_id=$(aws cloudformation list-stack-resources \
--stack-name "${stack_name}" \
| jq -r '.StackResourceSummaries[] | select(.LogicalResourceId=="WindowsServer") | .PhysicalResourceId')
password=$(aws ec2 get-password-data \
--instance-id "${instance_id}" \
--priv-launch-key "${key_path}" \
| jq -r '.PasswordData')
echo "hostname: ${hostname}"
echo "password: ${password}"
スクリプトの実行とリモートアクセス
実行権限を付与してスクリプトを実行すると次のようになります。
$ ./create-stack-windows-check-server.sh
{
"StackId": "arn:aws:cloudformation:ap-northeast-1:123456789012:stack/WindowsCheckServer/30f72890-a6ac-11e8-ace0-50a686699abc"
}
hostname: ec2-54-238-142-250.ap-northeast-1.compute.amazonaws.com
password: qsF!l7iBU9*O.r)aw*Fxq;6NHIEVz3Y0
任意のリモートデスクトップクライアントを使用して、表示されたパスワードでホストに接続すれば、無事に初期設定済みのWindows環境が手に入ります。
使い終わったらスタックの削除を忘れずに!
まとめ
CloudFormatonテンプレートを作成する中で、知らなかったこともいろいろ知ることができ、勉強になりました。一度作ってしまえば使い回しは容易ですし、何より便利ですね。
今回のテンプレートでは、先日リリースされたばかりのT3インスタンスをさっそく取り入れてみました。検証エントリも上がっていますので、ぜひご覧ください。