воскресенье, 31 марта 2013 г.

Powershell и SQL Azure.

Powershell уже давно стал универсальным "орудием труда" для решения различного рода проблем/задач. И команда Windows Azure не отстает. Возможности управления своей подпиской через Powershell постоянно развиваются и модифицируются.

В данном блог посте речь пойдет конкретно о работе с SQL Azure из Powershell. Основные возможности рассмотрим на примерах, кое-где в сравнении с тем, как это можно сделать через Preview версию management портала.

Для старта понадобятся:

1. Powershell 3.0 - команды для управления SQL Azure выполняются минимум с версии 3.0.
2. Windows Azure Powershell - "ажурная" Powershell консоль, с которой в комплекте идут нужные скрипты, которые мы будем импортить. Её можно скачать по указанной ссылке, но лично я пользуюсь Web Platform Installer, что значительно удобнее.
3. Последняя версия Windows Azure SDK. В моем случае - это 1.8.

Позаботьтесь также об удобном Powershell редакторе. В моем случае - это PowershellGUI. Но подойдет хоть блокнот - лишь бы вам было комфортно.

Как вы наверняка знаете, Powershell имеет verb-noun синтаксис. Следовательно, существует перечень команд Get/Set/New/Remove.
Пройдемся по списку команд.
Команда Описание
Get-AzureSqlDatabaseServer Возвращает информацию о SQL Azure сервере по указанному имени. Если имя не указано, вернутся все доступные в подписке сервера.
Get-AzureSqlDatabase Возвращает базу данных по указанному имени в конексте указанного сервера. Если же имя БД не указано - возвращает все БД на указанном сервере.
Get-AzureSqlDatabaseServerFirewallRule Возвращает информацию о SQL Azure сервере по указанному имени. Если имя не указано, вернутся все доступные в подписке сервера.
New-AzureSqlDatabaseServer Создает экземпляр Azure SQL Server рамках подписки.
New-AzureSqlDatabase Создает новую БД в контексте указанного сервера.
New-AzureSqlDatabaseServerFirewallRule Добавляет правило работы firewall для указанного сервера в подписке.
Remove-AzureSqlDatabase Удаляет базу данных в указанном контексте.
Remove-AzureSqlDatabaseServer Удаляет указанный экземпляр SQL Azure сервера.
Remove-AzureSqlDatabaseServerFirewallRule Удаляет добавленное правило для firewall в контексте указанного сервера.
Set-AzureSqlDatabase Вносит изменения в настройки указанной БД - имя, макс. размер и т.д.
Set-AzureSqlDatabaseServer Вносит изменения в настройки SQL Azure сервера. Сейчас это только возможность изменить пароль администратора.
Set-AzureSqlDatabaseServerFirewallRule Вносит изменения в уже созданное firewall правило.

Некоторые из этих команд рассмотрим ниже. Об остальных же достаточно информации можно найти по указанным ссылкам на MSDN.

Итак, открывайте свой редактор и поехали.

С чего начнем


Первым делом необходимо импортировать модуль, который позволит нам работать с SQL Azure Powershell командами.
Import-Module 'C:\Program Files (x86)\Microsoft SDKs\Windows Azure\PowerShell\Azure\Azure.psd1'
Расположение этого модуля видно по коду. И вот, собственно, одна из причин, зачем необходимо было установить Azure Powershell.

Выбор подписки


Теперь сконфигурируем подписку, с которой планируем работать. Для этого есть два варианта:
1. Импортировать настройки (subscription id, certificate) из publishsettings файла.
2. Установить необходимые параметры, включая сертификат, вручную.

Воспользуемся первым вариантом. Если у вас есть скачанный pubslishsettings файл - отлично.
Если же нет - один из удобных вариантов его получения - команда Get-AzurePublishSettingsFile. Она запустит браузер по нужной ссылке, где вы сможете скачать файл с настройками.
Импортированные/заданные настройки в итоге будут храниться в системе: C:\Users\username\AppData\Roaming\Windows Azure Powershell.
Позаботьтесь также, чтобы файл с настройками не был readonly. Я не вдавался в подробности, но в моем случае readonly файл был причиной для ошибок. Поэтому просто стоит принять к сведению.

Файл готов? Тогда к делу.
Import-AzurePublishSettingsFile "pathToPublishSettingsFile"
Set-AzureSubscription -SubscriptionName "nameOfSubscription"
Если убрать вторую строку из примера, то будет выбрана подписка по умолчанию.
Прошло без ошибок? Значит мы готовы для работы с SQL Azure через Powershell.

Создание сервера


Для начала попробуем получить список доступных в подписке серверов SQL Azure.

На портале:



Через Powershell:

$availableServers = Get-AzureSqlDatabaseServer

Затем, создадим экземпляр SQL Azure Server.

Через портал:


Как видно, чекбокс, позволяющий Azure Web сервисам из подписки обращаться к новосозданному серверу - включен.

Через Powershell:
$newSqlServer= New-AzureSqlDatabaseServer -Location "WEST EUROPE" -AdministratorLogin "testUser" -AdministratorLoginPassword "testWorld123"

Firewall


По умолчанию Azure SQL Server позвляет к себе подключиться только IP адресам, указанным как разрешенные в firewall.
Зайдем в конфигурацию нового сервера, созданного через Powershell - в правилах пусто. Стоит обратить внимание, что к данному серверу запрещено подключение Azure сервисам из подписки.

Разработчики не предусмотрели такого параметра для New-AzureSqlDatabaseServer, хотя я считаю, что именно там ему и место.
На помощь приходит Azure Firewall. Если добавить правило, в котором в качестве IP Address будет указан 0.0.0.0, то Azure интерпретирует его именно как разрешение сервисам внутри облака подключаться к данному экземпляру SQL Server.
$newSqlServer| New-AzureSqlDatabaseServerFirewallRule -RuleName "AzureServices" -StartIpAddress "0.0.0.0" -EndIpAddress "0.0.0.0"
Также добавим в список правил IP адрес машины, с которой мы планируем далее выполнять наш скрипт. Иначе же при создании контекста все свалится с ошибкой доступа.

Работа с контекстом


New-AzureSqlDatabaseServerContext - создает соединение к указанному серверу.
Для подключения нам понадобятся учетные данные администратора, которые мы указывали при создании сервера.

Создадим объект класса PSCredential и передадим иму имя и пароль. Рекомендую также взглянуть на полезную команду Get-Credential, которая позволит запрашивать имя и пароль непосредственно при выполнении скрипта.
Займемся созданием контекста:
$servercredential = new-object System.Management.Automation.PSCredential("testUser", ("testWorld123"|  ConvertTo-SecureString -asPlainText -Force))

#Вариант 1.
$serverContext = $newSqlServer | New-AzureSqlDatabaseServerContext -Credential $serverCredential

#Вариант 2.
$serverContext = $New-AzureSqlDatabaseServerContext -ServerName "ywdddhc9qo" -Credential $serverCredential
В первом варианте - используем уже имеющуюся переменную, где есть вся информация о нашем сервере. Во втором - имя сервера (возможно также указать имя полностью либо ссылку).

Контекст получен. Теперь создадим базу данных.

В Powershell это выглядит вот как:
$testDB = New-AzureSqlDatabase $serverContext -DatabaseName "testDB" –Edition "Web" -MaxSizeGB 1 -Collation "SQL_Latin1_General_CP1_CI_AS"

Обязательными в данной команде являются контекст и имя новой БД. Если не указать другие параметры, то их значения будут установлены по умолчанию.

Через портал же это выглядит вот так. И само собой, руками контекст нам получать не надо.

Указываем имя, версию (WEB/Business), максимальный размер, collation, подписку и сервер в указанной подписке.

Теперь получим список с имеющимися на сервере БД. Для этого воспользуемся командой Get-AzureSqlDatabase. Если имя БД указано, то команда попытается найти конкретную базу. Если же не указано - вернет все имеющиеся.
$allDBs = Get-AzureSqlDatabase $serverContext

Выполнение SQL скриптов


Теперь, когда процесс и стандартные возможности работы с SQL Azure ясны, хотелось бы еще написать о работе со структурой базы.
Конечно, можно подключаться к нашей БД через SQL Server Management Studio либо через Management portal, где и выполнять нужный SQL при малейшем изменении. Но если уж автоматизировать, то весь процесс.

К сожалению, хранить весь SQL в одном файлике - не выйдет. Дело в том, что в SQL Azure есть набор команд, которые могут быть единственными в пакете. Если же это правило нарушить - получим ошибку.

В целом, не проблема - будем хранить скрипты в нескольких SQL файлах. Теперь вопрос, как, собственно, выполнить эти скрипты? Для этого воспользуемся sqlcmd.
В Powershell для этого есть команда Invoke-Sqlcmd. Перед её использованием нужно будет добавить пару оснасток (snap-in).

Invoke-Sqlcmd позволяет задать файл, в котором лежит SQL скрипт, а также есть возможность вписать непосредственно Query для выполнения. Параметры взаимоисключающие.
В качестве параметра ServerInstance необходимо передавать полное имя нашего сервера, то есть включая "database.windows.net".

Ну что ж, приступим.
#Добавляем оснастки для работы с SQL Server
Add-pssnapin sqlserverprovidersnapin100
Add-pssnapin sqlservercmdletsnapin100

#Выполняем имеющийся SQL скрипт
$serverName = "ywdddhc9qo" + ".database.windows.net"
Invoke-Sqlcmd -InputFile "pathToSQLFile" -ServerInstance $serverName -Database "testDB" -Username "testUser" -Password "testWorld123"

Подведем итоги


Как вы убедились/знаете, все описанное в посте можно сделать и через портал/management studio. Но когда кол-во БД и серверов возрастает либо в вашем распоряжении несколько environment-ов (DEV/QA/ProdDemo/PROD), то на помощь как раз и приходит столь полезный инструмент для автоматизации.
И все самые необходимые возможности команда Windows Azure нам уже предоставила.

Будем следить за дальнейшим развитием.

вторник, 19 марта 2013 г.

SharePoint SPUtility - особенности отправки почты

Такая простая на первый взгляд задача, как отправка почты, может привнести пару новых багов в вашем проекте :) Речь пойдет об одном из стандартных подходов в SharePoint 2010 - SPUtility.SendEmail. Описание этого метода можно легко найти на MSDN. Но не надейтесь увидеть там много букв, оно довольно таки скудное. Начнем с того, что работает, как ожидается.
SendEmail(SPWeb, Boolean, Boolean, String, String, String)
SendEmail(SPWeb, Boolean, Boolean, String, String, String, Boolean)
Проблем и опасений не вызывают. Почта отлично отсылается, если в Central Administration (далее CA) указаны корректные настройки.
Но вот интерес вызывают как раз первые две реализации метода:
SendEmail(SPWeb, StringDictionary, String)
SendEmail(SPWeb, StringDictionary, String, Boolean)
Вторым параметром оба метода принимают словарь из строк, чтобы добавить в отправляемое письмо нужный набор header-ов. Накидаем простенький пример:
    var headers = new StringDictionary
    {
       { "to", "samplemail@sample.com" },
       { "subject", "Test subject" },
       { "content-type", "text/html" }
    };

    SPUtility.SendEmail(web, headers, bodyText);
И тут выясняется некая особенность. Если мы передаем свой набор header-ов, то выбор email-a, с которого нужно эту самую почту отправить - работает несколько по другому. Если в контексте указанного web-а, у пользователя в настройках указан email, то почта попытается уйти именно с email-а пользователя. Если же такой email не указан, то берется адрес, указанный в CA. Решить эту ситуацию можно довольно просто - необходимо явно добавлять в словарь header "from", где и указывать необходимый адрес отправителя. В данном случае - это адрес из CA.
    var headers = new StringDictionary
    {
       { "to", "samplemail@sample.com" },
       { "from", web.Site.WebApplication.OutboundMailSenderAddress },
       { "subject", "Test subject" },
       { "content-type", "text/html" }
    };
Теперь почта уходит от корректного отправителя.