こんにちは、Enterprise Cloud部 技術1課 宮形 です。今回BLOGでは、Amazon EC2 を利用して Windows IIS と SQLServer の操作を体験し、小さい動的Webサイトのサンプルを作成する一連流れをご紹介いたします。下記はWebサイトの完成したイメージです。
経緯
Amazon EC2 には Microsoft SQL Server (以下 SQL Server と記)がインストール済でライセンスもAWS利用料に組み込まれたAMIが用意されています。こちらを利用するとセットアップ時間短縮や、ライセンス調達手間を省くことができます。
弊社でもこの SQL Server込みのEC2で環境構築することがありますが、OSやミドルウェアはお客様やベンダー様が利用するケースが殆どで、弊社メンバーはSQL Server がどのように利用されるかを知る機会があまりありません。そこで社内で下記タイトルでワークショップ形式の勉強会を行うことになりました。
- Windows IISとSQLServerで作る動的Webサイト
本BLOGはその時の内容ご紹介となります。BLOGご覧なった方でAWSをご利用できる環境があり、IIS や SQL Server を触ってみたい!という方は是非ご参考いただけると幸いです。
構成図
ワークショップ環境として利用するAWSの構成図は下記のようになります。
パブリックサブネットに EC2 として SQL Server 込みの Windows Server を配置し、パブリックIPアドレス宛てに直接 Windows Server へ接続する構成です。今回は手っ取り早くEC2を利用するためにこのようにしましたが、本番業務ではセキュリティや運用考慮してEC2はプライベートサーブネットに配置することが多いと思います。お手元環境からリモートデスクトップで接続できるのであれば、プライベートサブネットに配置しても大丈夫です。
環境を作ってみる
下記の順番で環境を作っていただくことになります。
- AWS環境の構築
- IISインストール
- SQLServerの設定変更
- データベースのテーブルよりデータを表示するプログラム配置
AWS環境の構築
SQL Server 込みの Windows Server の Amazon マシンイメージ (AMI) を選んで EC2 インスタンスを起動します。
既にVPCやサブネットがあり、お手元パソコンからリモートデスクトップで接続できるのであれば、そちらにEC2を配置します。
VPCやサブネットが無い場合や、ワークショップ用に作りたい場合は、本BLOG末尾にサンプルの CloudFormation テンプレートを添付しているのでご利用ください。
お手元パソコンからリモートデスクトップ出来るところまで進めます。
IIS のインストール
サーバーマネージャーの「役割の追加」を開始します。
「サーバーの役割の選択」より「Webサーバー(IIS)」をチェックします。
「次へ」で画面を進めます。「役割サービスの選択」で「アプリケーション開発 - ASP.NET 4.7」をチェックします。
「次へ」で画面を進め「インストール」を押下します。しばらくすると「・・・でインストールが正常に完了しました。」となります。
http://(EC2のパブリックIPアドレス or プライベートIPアドレス)
へアクセスして、IISデフォルトページが表示されることを確認します。
プログラムのサンプルで動的Webサイトが表示できることを確認します。下記例では c:\inetpub\wwwroot\hello.aspx
というファイルを作成し、コードを記載します。
<%@ Page Language="C#" %>
<html>
<body>
<% Response.Write("Hello World"); %>
</body>
</html>
http://(EC2のパブリックIPアドレス or プライベートIPアドレス)/hello.aspx
へアクセスします。Hello World が表示されれば ASP.NETというフレームワークの C#言語でアプリケーションが動作していることが確認できます。
SQL Server の設定変更
SQL Server 込みの Windows Server のAMIから EC2 を起動しているので、はじめから Windows Server に SQL Server がインストールされており利用可能です。最低限のデータベースとテーブル、認証の設定を行います。Windows Server のスタートメニューより「SQL Server Management Studio」を開始します。
「サーバーへの接続」は自動的に Windows 認証が選ばれているので、そのまま「接続」します。
最初にデータベースを作成します。左ペインの「オブジェクト エクスプローラー」より「データベース」- 右クリック「新しいデータベース」を押下します。
「新しいデータベース」の画面でデータベース名を命名し、「OK」を押下します。本例では study
としています。
続けてテーブルを作成します。左ペインより先ほど作成したデータベースを選択し、右クリック「新規作成」-「テーブル」を押下します。
「列名」「データ型」を入力する画面となります。ワークショップなので何でもよくお好みで設定します。本例では下記のようにしました。「保存」ボタンを押下すると、テーブル名の入力になります。本例では employe
としました。
列名 |
データ型 |
id |
numeric (18, 0) |
name |
char(10) |
作成したテーブルにサンプルデータを入力します。左ペインより先ほど作成したテーブルを選択し、右クリック「上位 200行の編集」を押下します。スプレッドシートが起動するので、サンプルデータを入力します。
「上位 1000行の選択」を押下すると、今ほど入力したサンプルデータが表示されます。これで最低限のデータベース、テーブルの準備は完了です。
SQL Server インスタンスの認証モードを変更します。左ペインの最上部を選択し、右クリック「プロパティ」を押下します。
プロパティより「セキュリティ」-「サーバー認証」を「SQL Server 認証モード と Windows 認証モード」を選択し、「OK」を押下します。
左ペイン「セキュリティ」-「ログイン」より「sa」を選択し、右クリック「プロパティ」を押下します。プロパティより「状態」-「ログイン」を「有効」にします。
続けて「全般」よりsaユーザーのパスワードを新規設定する箇所があるので入力し「OK」を押下します。設定したパスワードは覚えておきます。
「管理ツール」-「サービス」より SQL Server (MSSQLSERVER)
のサービスを再起動します。
データベースのテーブルよりデータを表示するプログラム配置
先ほどのサンプルと同様にプログラムを配置します。本例では c:\inetpub\wwwroot\employe.aspx
というファイルを作成し、コードを下記のように記載します。●●の箇所は先ほど設定したsaユーザーのパスワードとします。テーブル名が employe
以外の場合も修正します。 (実際のシステムではデータベース接続情報はプログラム単体には記述せず、共通設定ファイルなどで管理することが多いと思います。また sa ユーザーは権限が強すぎるので、アプリケーション接続用には別ユーザーを用意した方がセキュリティ的にも良いです。)
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<script runat="server">
protected void Page_Load(Object source, EventArgs e) {
if (IsPostBack) {
return;
}
// 接続文字列
string connectString = "Data Source=.;";
connectString += "Initial Catalog=study;";
connectString += "User ID=sa;";
connectString += "Password=(●先ほど入力したパスワード●);";
connectString += "Trusted_Connection=False;";
// 取得したデータ退避
DataTable dt = new DataTable();
using (SqlConnection con = new SqlConnection(connectString))
using (SqlDataAdapter adapter = new SqlDataAdapter()) {
con.Open();
// クエリを実行しDataTableに格納
string query = "SELECT * FROM employe";
adapter.SelectCommand = new SqlCommand(query, con);
adapter.Fill(dt);
}
// DataGridとバインド
datagrid.DataSource = new DataView(dt);
DataBind();
}
</script>
<html>
<body>
<asp:DataGrid id="datagrid" runat="server" />
http://(EC2のパブリックIPアドレス or プライベートIPアドレス)/employe.aspx
へアクセスします。先ほど作成したデータベース・テーブルのサンプルデータが表示されます。「SQL Server Management Studio」からサンプルデータを追加、変更、削除などしてみて、ブラウザを再表示するとデータが更新されることがわかると思います。
これで小さいながらも IIS と SQL Serverを使った動的Webサイトの完成となります。おつかれさまでした!
まとめ
サーバーワークスに入社してから OS や ミドルウェアを触る機会が殆どなくなっていたので、今回BLOG内容のワークショップ準備をすること自体がとても楽しかったです。このようなワークショップ環境をサクっと短時間で作れてしまうのも、AWSクラウドのメリットだと感じました。
本BLOGの内容が「AWSはよく利用するけど、ミドルウェアに触れる機会が無い」という方向けに、スキル範囲を広げる取っ掛かりになればよいと思います。皆様の参考になれば幸いです。
VPC、Security Group、EC2 を一括で作る CloudFormation テンプレートを用意しているのでご利用ください。
キーペア名とご自宅ご職場のパブリックIPアドレスをパラメーター入力する箇所があるので、適宜ご用意のうえ入力ください。
キーペアを作成する - Amazon Elastic Compute Cloud
https://checkip.amazonaws.com/
AWSTemplateFormatVersion: "2010-09-09"
Description:
"Environment for Study"
Parameters:
CustomerName:
Description: A system name that is prefixed to resource names
Type: "String"
Default: "study"
EnvironmentName:
Description: An environment name that is prefixed to resource names
Type: "String"
AllowedValues:
- "prod"
- "stg"
- "dev"
Default: "stg"
KeyPairName:
Description: "Enter Keypair Name"
Type: "String"
EC2AmiId:
Description: "Enter EC2 Ami ID (ex. ami-xxxxxxxxxxxxxxxxx)"
Type: "String"
Default: "ami-016c5c49b07712ec0"
YourPubIP:
Description: "Enter Your Public IP Address (ex. 123.123.123.123/32)"
Type: "String"
Resources:
VPC:
Type: 'AWS::EC2::VPC'
DeletionPolicy: Delete
Properties:
CidrBlock: 10.10.0.0/16
EnableDnsHostnames: 'true'
EnableDnsSupport: 'true'
Tags:
- Key: Name
Value: !Sub "${CustomerName}-${EnvironmentName}-vpc"
SubnetPublicA:
Type: 'AWS::EC2::Subnet'
DeletionPolicy: Delete
Properties:
VpcId: !Ref VPC
AvailabilityZone: ap-northeast-1a
CidrBlock: 10.10.0.0/24
MapPublicIpOnLaunch: 'false'
Tags:
- Key: Name
Value: !Sub "${CustomerName}-${EnvironmentName}-sub-public-a"
SubnetPublicC:
Type: 'AWS::EC2::Subnet'
DeletionPolicy: Delete
Properties:
VpcId: !Ref VPC
AvailabilityZone: ap-northeast-1c
CidrBlock: 10.10.1.0/24
MapPublicIpOnLaunch: 'false'
Tags:
- Key: Name
Value: !Sub "${CustomerName}-${EnvironmentName}-sub-public-c"
SubnetPrivateA:
Type: 'AWS::EC2::Subnet'
DeletionPolicy: Delete
Properties:
VpcId: !Ref VPC
AvailabilityZone: ap-northeast-1a
CidrBlock: 10.10.2.0/24
MapPublicIpOnLaunch: 'false'
Tags:
- Key: Name
Value: !Sub "${CustomerName}-${EnvironmentName}-sub-private-a"
SubnetPrivateC:
Type: 'AWS::EC2::Subnet'
DeletionPolicy: Delete
Properties:
VpcId: !Ref VPC
AvailabilityZone: ap-northeast-1c
CidrBlock: 10.10.3.0/24
MapPublicIpOnLaunch: 'false'
Tags:
- Key: Name
Value: !Sub "${CustomerName}-${EnvironmentName}-sub-private-c"
Igw:
Type: 'AWS::EC2::InternetGateway'
DeletionPolicy: Delete
Properties:
Tags:
- Key: Name
Value: !Sub "${CustomerName}-${EnvironmentName}-igw"
IgwAttach:
Type: 'AWS::EC2::VPCGatewayAttachment'
DeletionPolicy: Delete
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref Igw
RtbPublic:
Type: 'AWS::EC2::RouteTable'
DeletionPolicy: Delete
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${CustomerName}-${EnvironmentName}-rtb-public"
RtbPublicRoute0:
Type: 'AWS::EC2::Route'
DependsOn: IgwAttach
DeletionPolicy: Delete
Properties:
RouteTableId: !Ref RtbPublic
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref Igw
RtbPrivate:
Type: 'AWS::EC2::RouteTable'
DeletionPolicy: Delete
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${CustomerName}-${EnvironmentName}-rtb-private"
SubnetRouteTableAssociationSubnetPublicA:
Type: 'AWS::EC2::SubnetRouteTableAssociation'
DeletionPolicy: Delete
Properties:
SubnetId: !Ref SubnetPublicA
RouteTableId: !Ref RtbPublic
SubnetRouteTableAssociationSubnetPublicC:
Type: 'AWS::EC2::SubnetRouteTableAssociation'
DeletionPolicy: Delete
Properties:
SubnetId: !Ref SubnetPublicC
RouteTableId: !Ref RtbPublic
SubnetRouteTableAssociationSubnetPrivateA:
Type: 'AWS::EC2::SubnetRouteTableAssociation'
DeletionPolicy: Delete
Properties:
SubnetId: !Ref SubnetPrivateA
RouteTableId: !Ref RtbPrivate
SubnetRouteTableAssociationSubnetPrivateC:
Type: 'AWS::EC2::SubnetRouteTableAssociation'
DeletionPolicy: Delete
Properties:
SubnetId: !Ref SubnetPrivateC
RouteTableId: !Ref RtbPrivate
sg01:
Type: "AWS::EC2::SecurityGroup"
DeletionPolicy: "Delete"
Properties:
GroupDescription: !Sub "for ${CustomerName}-${EnvironmentName}-svr"
GroupName: !Sub "${CustomerName}-${EnvironmentName}-sg-svr"
Tags:
- Key: "Name"
Value: !Sub "${CustomerName}-${EnvironmentName}-sg-svr"
VpcId: !Ref VPC
sg01Ingress0:
Type: "AWS::EC2::SecurityGroupIngress"
DeletionPolicy: "Delete"
Properties:
Description: "HTTP access from your location"
FromPort: 80
ToPort: 80
GroupId: !Ref sg01
IpProtocol: "tcp"
CidrIp: !Ref YourPubIP
sg01Ingress1:
Type: "AWS::EC2::SecurityGroupIngress"
DeletionPolicy: "Delete"
Properties:
Description: "HTTPS access from your location"
FromPort: 443
ToPort: 443
GroupId: !Ref sg01
IpProtocol: "tcp"
CidrIp: !Ref YourPubIP
sg01Ingress2:
Type: "AWS::EC2::SecurityGroupIngress"
DeletionPolicy: "Delete"
Properties:
Description: "RDP access from your location"
FromPort: 3389
ToPort: 3389
GroupId: !Ref sg01
IpProtocol: "tcp"
CidrIp: !Ref YourPubIP
sg01Ingress3:
Type: "AWS::EC2::SecurityGroupIngress"
DeletionPolicy: "Delete"
Properties:
Description: "ssh access from your location"
FromPort: 22
ToPort: 22
GroupId: !Ref sg01
IpProtocol: "tcp"
CidrIp: !Ref YourPubIP
sg01Egress0:
Type: "AWS::EC2::SecurityGroupEgress"
DeletionPolicy: "Delete"
Properties:
Description: "no limit access to anywhere"
FromPort: 1
ToPort: 65535
GroupId: !Ref sg01
IpProtocol: "-1"
CidrIp: 0.0.0.0/0
Server:
Type: AWS::EC2::Instance
Properties:
InstanceInitiatedShutdownBehavior: "stop"
EbsOptimized: true
Monitoring: false
SourceDestCheck: true
Tenancy: "default"
KeyName: !Ref KeyPairName
InstanceType: "t3a.xlarge"
ImageId: !Ref EC2AmiId
Tags:
- Key: "Name"
Value: !Sub "${CustomerName}-${EnvironmentName}-ec2-svr"
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
DeleteOnTermination: true
VolumeSize: 100
VolumeType: gp3
NetworkInterfaces:
- AssociatePublicIpAddress: true
DeviceIndex: 0
SubnetId: !Ref SubnetPublicA
GroupSet:
- !Ref sg01
LaunchTemplate:
LaunchTemplateId: !Ref EC2Template
Version: 1
EC2Template:
Type: AWS::EC2::LaunchTemplate
Properties:
LaunchTemplateData:
MetadataOptions: #IMDSv2
HttpTokens: required
TagSpecifications:
- ResourceType: volume
Tags:
- Key: Name
Value: !Sub "${CustomerName}-${EnvironmentName}-ec2-svr"
宮形純平(執筆記事の一覧)
エンタープライズクラウド部 ソリューションアーキテクト1課
好きなお酒は缶チューハイと本格焼酎