Windows Azure の Virtual Machine (仮想マシン) のギャラリーには SQL Server がインストール済みのイメージがいくつか用意されています。
以前、Azure の VMs の SQL Server はどのようにして展開されているか という投稿で、どのように展開されているか軽く見てみましたが、今回の投稿では Windows Server 2012 + SQL Server 2012 の組み合わせでさらに詳細に見てみたいと思います。
# SQL Server 2008 R2 も同様の方法で展開しているようですが。
なお、この展開方法ですが現状は BOL には載っていないアンドキュメントなセットアップのアクションを使用しています。
Azure VM の SQL Server のセットアップログですが、デプロイが完了すると以下のような状態になります。
最初のログが SQL Server をセットアップした際のログ、2 番目のログがデプロイ後に SQL Server の設定を変更する際のログとなります。
Azure VM の SQL Server のセットアップは SysPrep を使用した SQL Server 2012 のインストール ではなく、SQL Server がインストールされた環境で Windows の Sysprep を実行し、初期の起動時にタスクスケジューラー経由で、[ACTION=”ConfigureImage”] というインストールアクションを使用して初期設定を行っているようです。
ConfigureImage を使用した展開は、Azure VM だけでなく自分でインストールをした SQL Server でも利用することはできるのですが、いろいろと準備をする必要があります。
■Windows Server 2012 で使用する場合には SQL Server 2012 SP1 CU 5 以降 を使用する。
SQL Server 2012 SP1 CU5 では、以下のような修正が行われています。
FIX: The ConfigureImage workflow is not triggered for Windows 2012 images.
この修正を適用してインストールをするために、SP1 CU5 以降をスリップストリームインストールでインストールを行います。
SQL Server 2012 の場合は、2 種類の方法でスリップストリームインストールをすることができ、Azrure VM では、インストールメディア内のファイルを格納したフォルダ内に以下のフォルダを作成し、それぞれに Service Pack と Cumulative Update を格納しています。
- PCU : Service Pack の内容を展開 (/x: オプションを使用) したものを配置
- CU : Cumulative Update の内容を展開 (/x: オプションを使用) したものを配置
このフォルダを使用してインストールをするように Defaultsetup.ini を以下のように修正してインストールをしています。
;SQL Server 2012 Configuration File [OPTIONS] PCUSOURCE=".PCU" CUSOURCE=".CU"
これでスリップストリームインストールを実施します。
■SQL Server の管理者アカウントは [SYSTEM] を使用する
Azure VM の [Setup Bootstrap] 内のインストールの構成ファイルを確認するとわかるのですが、Azure VM の SQL Server はインストール時の管理者アカウントは [SYSTEM] でインストールをされています。
; Windows account(s) to provision as SQL Server system administrators. SQLSYSADMINACCOUNTS="NT AUTHORITYSYSTEM"
この状態ですと ローカルシステム (SYSTEM) しかログインができなくなりますが、構成を変更する際の ConfigureImage を使用したセットアップはタスクスケジューラーをローカルシステムで実行しますので、このアクションを使用したセットアップであればこの設定で問題はありません。
# ローカルシステムのほかにローカルの Administrator アカウントを設定していても問題はありません。
SQL Server では管理者アカウントがロックアウトされている状態でもローカルの Administrators グループのユーザーはログインできるようにすることができます。
システム管理者がロックアウトされた場合の SQL Server への接続
■タスクスケジューラーを設定
Azure VM の SQL Server では、ConfigureImage をアクションに指定したセットアップの実行はタスクスケジューラーから実行しています。
このタスクスケジューラーの配置ですが、いくつかのルールがあります。
- ConfigureSqlImageTasks というタスクスケジューラーのフォルダを作成する。
- そのフォルダ内に RunConfigureImage というタスクを作成する。
このルールを守って、タスクスケジューラーを設定する必要があります。
1. / 2. の制約ですが、これはセットアップ実行時にタスクスケジューラーからタスクを削除するために必要となります。
# 2. もしかしたら名称が違っても大丈夫かもしれませんが、1. はこの名称でないと、実行後にタスクが削除されません。
アクションに ConfigureImage を指定した場合、セットアップの完了後にタスクの削除を行うのですが、この削除は ConfigureSqlImageTasks というフォルダを対象にします。
そのため、タスクスケジューラーには以下のようなディレクトリを作成しておきます。
Setup.exe を実行するタスクはこのディレクトリの下に作成をするのですが、設定については AzureVM の SQL Server のギャラリーから展開した VM の[C:Program FilesMicrosoft SQL Server110Setup BootstrapSQLServer2012x64] から [ScheduledTask.xml] をコピーしてしまうとよいかと思います。
実際には以下のような内容が設定されています。
<?xml version="1.0" encoding="UTF-16"?> <Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"> <RegistrationInfo /> <Triggers> <BootTrigger> <Enabled>true</Enabled> </BootTrigger> <EventTrigger> <Enabled>true</Enabled> <Subscription><QueryList><Query Id='0' Path='Application'><Select Path='Application'>*[System[Provider[@Name='Microsoft-Windows-Security-SPP'] and EventID=1003]]</Select></Query></QueryList></Subscription> </EventTrigger> </Triggers> <Principals> <Principal id="Author"> <UserId>S-1-5-18</UserId> <RunLevel>HighestAvailable</RunLevel> </Principal> </Principals> <Settings> <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy> <DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries> <StopIfGoingOnBatteries>true</StopIfGoingOnBatteries> <AllowHardTerminate>false</AllowHardTerminate> <StartWhenAvailable>false</StartWhenAvailable> <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable> <IdleSettings> <StopOnIdleEnd>true</StopOnIdleEnd> <RestartOnIdle>false</RestartOnIdle> </IdleSettings> <AllowStartOnDemand>true</AllowStartOnDemand> <Enabled>true</Enabled> <Hidden>false</Hidden> <RunOnlyIfIdle>false</RunOnlyIfIdle> <DisallowStartOnRemoteAppSession>false</DisallowStartOnRemoteAppSession> <UseUnifiedSchedulingEngine>false</UseUnifiedSchedulingEngine> <WakeToRun>false</WakeToRun> <ExecutionTimeLimit>PT72H</ExecutionTimeLimit> <Priority>7</Priority> </Settings> <Actions Context="Author"> <Exec> <Command>C:\Program Files\Microsoft SQL Server\130\Setup Bootstrap\SQLServer2016\setup.exe</Command> <Arguments>/ACTION=CONFIGUREIMAGE /USEMICROSOFTUPDATE /IACCEPTSQLSERVERLICENSETERMS /INSTANCENAME="MSSQLSERVER" /Q</Arguments> </Exec> </Actions> </Task>
この XML をタスクスケジューラーの [ConfigureSqlImageTasks ] フォルダ配下にインポートして、タスクの名称を [RunConfigureImage] に設定すれば、タスクスケジューラーの準備は完了です。
このタスクが実行されることで Sysadmin のメンバーのメンテナンスと、sp_dropserver / sp_addserver を使用したサーバー名の変更が行われます。
■手間をかけずに試すためには SQL Server の英語版を使用する
これが検証をする際に一番はまったのですが、手間をかけずに検証する場合には SQL Server の英語版 を使用することをお勧めします。
日本語版でもファイルの配置を変更すると、正常に動作させることができるのですが、SQL Server の英語版であれば、ファイル配置を変更しないでも正常に動作させることができます。
SQL Server の英語版をスリップストリームインストールで最新の CU を当てた状態で、インストールして、タスクスケジューラーのタスクが実行されると Setup Bootstrap には、以下のようなログが出力されます。
(01) 2013-09-22 10:10:01 Slp: Connection string: Data Source=\.pipeSQLLocalMSSQLSERVER;Initial Catalog=master;Integrated Security=True;Pooling=False;Connect Timeout=300;Network Library=dbnmpntw;Application Name=SqlSetup (01) 2013-09-22 10:10:03 Slp: Connected successfully... (01) 2013-09-22 10:10:03 Slp: Sco: Attempting to get machine administrator logon account name (01) 2013-09-22 10:10:03 Slp: Sco: Attempting to get sid for machine domain SQLDEPLOY (01) 2013-09-22 10:10:03 Slp: Sco: Attempting to get NT account from sid S-1-5-21-138499402-210688190-3290417106-500 (01) 2013-09-22 10:10:03 Slp: Sco: Attempting to get account from sid S-1-5-21-138499402-210688190-3290417106-500 (01) 2013-09-22 10:10:03 Slp: Sco: Attempting to get local system account name (01) 2013-09-22 10:10:03 Slp: Sco: Attempting to get NT account from sid S-1-5-18 (01) 2013-09-22 10:10:03 Slp: Sco: Attempting to get account from sid S-1-5-18 (01) 2013-09-22 10:10:04 Slp: Sco: Attempting to execute script /************************************************************/ -- Secript to Rename SqlServer name as machine name. -- /************************************************************/ SET NOCOUNT ON DECLARE @MachineName VARCHAR(100), @InstanceName VARCHAR(100), @ServerName VARCHAR(100), @local_machine_admin VARCHAR(100) -- Get the SQL Server installed Machine Name SET @MachineName = CONVERT(VARCHAR(100), SERVERPROPERTY('machinename')); -- Get the Sql Serve instance Name SET @InstanceName = CONVERT(VARCHAR(100),SERVERPROPERTY('instancename')); --Get the SQL Server name SET @ServerName = CONVERT(VARCHAR(100),(SELECT @@SERVERNAME)); --Get current logged-in user SET @local_machine_admin = 'SQLDEPLOYadmini'; IF (@ServerName != @MachineName) BEGIN -- check the instance name exist or not. IF ((@InstanceName is null) OR (@InstanceName = '')) BEGIN -- SQL Server instance name is empty -- Configure SQL Server new name as Machine -- Remove the Current Server name exec sp_dropserver @server=@ServerName,@droplogins='droplogins'; -- Add New server name exec sp_addserver @server=@MachineName,@local='local'; -- Print New Server name SELECT @MachineName; END ELSE BEGIN -- SQL Server has an instance name -- Configure SQL Server new name as Machine with intstance name. -- Remove the Current Server name exec sp_dropserver @server=@ServerName,@droplogins='droplogins';; -- Add New server name SET @ServerName = @MachineName + '' + @InstanceName; exec sp_addserver @server=@ServerName,@local='local'; -- Print New Server name SELECT @MachineName + '' + @InstanceName; END END BEGIN TRANSACTION if not exists (select loginname from [master].[dbo].[syslogins] where UPPER([name]) = UPPER(@local_machine_admin)) BEGIN ---Add Current logged-in user to sysadmin role, Drop BuiltinAdministrator group EXEC('CREATE LOGIN [' + @local_machine_admin + '] FROM WINDOWS WITH DEFAULT_DATABASE=[master]'); --Add sysadmin role to the created login EXEC('ALTER SERVER ROLE [sysadmin] ADD MEMBER [' + @local_machine_admin +']') END if exists (select loginname from [master].[dbo].[syslogins] where UPPER([name]) = UPPER('NT AUTHORITYSYSTEM')) and exists (select loginname from [master].[dbo].[syslogins] where UPPER([name]) = UPPER(@local_machine_admin)) BEGIN DROP LOGIN [NT AUTHORITYSYSTEM] END COMMIT --- Stop and start the SQL Server.
SQL Server の日本語版を使用した場合、Setup Bootstrap に出力されているログの情報は以下のようになります。
(01) 2013-09-22 20:23:44 Slp: Connection string: Data Source=\.pipeSQLLocalMSSQLSERVER;Initial Catalog=master;Integrated Security=True;Pooling=False;Connect Timeout=300;Network Library=dbnmpntw;Application Name=SqlSetup (01) 2013-09-22 20:23:45 Slp: Connected successfully... (01) 2013-09-22 20:23:45 Slp: Sco: Attempting to execute script /************************************************************/ -- Secript to Rename SqlServer name as machine name. -- /************************************************************/ SET NOCOUNT ON DECLARE @MachineName VARCHAR(100), @InstanceName VARCHAR(100), @ServerName VARCHAR(100), @current_user VARCHAR(100) -- Get the SQL Server installed Machine Name SET @MachineName = CONVERT(VARCHAR(100), SERVERPROPERTY('machinename')); -- Get the Sql Serve instance Name SET @InstanceName = CONVERT(VARCHAR(100),SERVERPROPERTY('instancename')); --Get the SQL Server name SET @ServerName = CONVERT(VARCHAR(100),(SELECT @@SERVERNAME)); --Get current logged-in user SET @current_user = SYSTEM_USER; IF (@ServerName != @MachineName) BEGIN -- check the instance name exist or not. IF ((@InstanceName is null) OR (@InstanceName = '')) BEGIN -- SQL Server instance name is empty -- Configure SQL Server new name as Machine -- Remove the Current Server name exec sp_dropserver @server=@ServerName,@droplogins='droplogins'; -- Add New server name exec sp_addserver @server=@MachineName,@local='local'; -- Print New Server name SELECT @MachineName; END ELSE BEGIN -- SQL Server has an instance name -- Configure SQL Server new name as Machine with intstance name. -- Remove the Current Server name exec sp_dropserver @server=@ServerName,@droplogins='droplogins';; -- Add New server name SET @ServerName = @MachineName + '' + @InstanceName; exec sp_addserver @server=@ServerName,@local='local'; -- Print New Server name SELECT @MachineName + '' + @InstanceName; END END BEGIN TRANSACTION if not exists (select loginname from [master].[dbo].[syslogins] where UPPER([name]) = UPPER(@current_user)) BEGIN ---Add Current logged-in user to sysadmin role, Drop BuiltinAdministrator group EXEC('CREATE LOGIN [' + @current_user + '] FROM WINDOWS WITH DEFAULT_DATABASE=[master]'); --Add sysadmin role to the created login EXEC('ALTER SERVER ROLE [sysadmin] ADD MEMBER [' + @current_user +']') END if exists (select loginname from [master].[dbo].[syslogins] where UPPER([name]) = UPPER('BUILTINAdministrators')) BEGIN DROP LOGIN [BUILTINAdministrators] END COMMIT --- Stop and start the SQL Server.
英語版の SQL Server を使用した場合には、ローカルシステムアカウントの削除と、Administrators グループのアカウントが Sysadmin として権限付与されるのですが、日本語版の SQL Server では、Administrators グループが削除されるようになっています。
この差ですが、英語と日本語 (正確には日本語以外) で Setup Bootstrap 内に保存されるファイルのバージョンが異なるために発生しているようです。
Setup Bootstrap 内にはインストールに使用したセットアップ等が保存され、タスクスケジューラーでは Setup Bootstrapp 内の Setup.exe を実行して ConfigureImage のアクションを実行しています。
この際、Setup Bootstrap (C:Program FilesMicrosoft SQL Server110Setup BootstrapSQLServer2012) には PCU / CU の言語に応じた最新のモジュールが保存されるようなのですが、英語版の場合には、CU のモジュール (PCU に対しての CU なので最新) が配置されるのですが、日本語の場合には PCU のモジュールが配置され、ConfigureImage の修正が入っている CU のセットアップファイルがコピーされていないために挙動が変わってくるようです。
# 最初、これがわからなくてドハマリしていました。
CU のディレクトリ構成ですが、英語 (1033) と日本語 (1041) では以下のような構成になっています。
日本語の言語のフォルダには [setup] が配置されていません。
このため、日本語の SQL Server をインストールした場合には、CU の内容が Setup Bootstrap にコピーされずに、ConfigureImage の挙動が変わってくるようです。
この現象を回避するためには、1033 の setup を 1041 にコピーしてから、SQL Server のインストールを行います。
これで日本語の SQL Server でも Setup Bootstrap 配下に CU の最新のモジュールが格納されます。
これで準備は完了です。
タスクスケジューラーのジョブを実行すると、Sysprep 後の環境に合わせて SQL Server の構成が変更されます。
SQL Server のスタンドアロン インスタンスをホストするコンピューターの名前変更 の設定変更 + α をやっている感じなのでしょうかね。
ConfigureImage のアクションについてはまだドキュメントに記載されていないアンドキュメントなものですが、大量に展開する際には便利なので、公開情報を期待したいですね。
SQL Server の Sysprep だと SSMS 等をインストールしているとダメだったりと制約があったはずですのでできればこちらを使っていきたいですね。