Автоматическое развертывание Windows Failover кластеров в Azure и еще один пример неработающего публичного кода и поддержки от Microsoft


Смотри на действование Microsoft: ибо кто может выпрямить то, что Он сделал кривым? (Екк. 7:13)

Верить в наше время нельзя никому, порой даже самому себе. Мне — можно. (Старина Мюллер)

Северный пушной зверек подкрался незаметно, откуда его никто не ждал… (Работа)

Начало этой истории (которая по странному стечению обстоятельств практически совпала с другой историй про Microsoft support, о которой я писал ранее и с еще более печальной историей про бан моего курса по экзамену AZ-900 со стороны Microsoft) было драматичным… Заказчик присылает на развертывание дизайн ресурсной группы в Azure, в котором кроме всего прочего – еще и 6 Windows Failover Clusters по 2 сервака в каждом и с обычными сроками «на завтра». В принципе, все есть – ARM templates много раз деланы-переделаны, отдельные ARM template/DSC для кластеров официально опубликованы Microsoft на GitHub – https://github.com/Azure/azure-quickstart-templates/tree/master/301-storage-spaces-direct . Звоню заказчику, торгуюсь по срокам, договариваемся «на послезавтра, иншааллах». В моем отделе не принято «сетапить ручками» или сдавать заказчику непроверенные пакет ARM/DSC/PowerShell скриптов – потому день будет на тестирование «на всякий случай», хотя скрипты уже множество раз протестированы и отлажены на предыдущих деплойментах, в том числе и для этого заказчика буквально месяц назад – в общем, на 100% уверены и тестовое развертывание – это «чистая формальность»…

Ага! «Формальность»! Как же! До того момента, как следом за Microsoft с его комплектом ARM/DSC/PowerShell для развертывания кластера на GitHub не подкрался тот самый северный пушной зверек. Народ сразу взялся за дело и … смотрит на меня слегка офигевшими от прихода зверька глазами. Microsoft’овский скрипт для развертывания кластера выдает при тестовом прогоне ошибку:

“DSC Configuration \u0027ConfigS2D\u0027 completed with error(s). Following are the first few: PowerShell DSC resource MicrosoftAzure_xCluster failed to execute Set-TargetResource functionality with error message: The running command stopped because the preference variable “ErrorActionPreference” or common parameter is set to Stop: The network path was not found.\r\n The SendConfigurationApply function did not succeed. LCM failed to start desired state configuration manually.”

и деплоймент всего ARM шаблона обваливается 😦 Понятно, что «на послезавтра» сделать кластеры можно, но «ручками», а это не наш стиль – все инфраструктуры клиентов у нас хранятся в ARM/DSC/PowerShell и даже если нет полной копии сайта в Azure Site Recovery, а только бекапы – восстановление/развертывание копии инфраструктуры заказчика с наличием таких скриптов занимает от минут до нескольких часов.

И вот тут до меня начинает доходить весь круг тех проблем, которые очертил своими лапками пушной зверек, вызванный нежеланием Microsoft сопровождать и тестировать свои скрипты после очередных апдейтов (типа, мы вам их «кинули» 3 года назад и чего это вы еще недовольны?). А проблема в том, что примерно в 1/3 от всех развернутых клиентских инфраструктур у нас присутствуют кластеры и это означает, что случись сейчас что или клиенты захотят «дубликаты» – то у нас нет рабочих скриптов «быстро все повторить». В общем, «зверек» разгулялся по полной…

Быстрое изучение вопроса и поиск решения показало только то, что проблема не только у нас, но есть еще люди на просторах Интернета и даже успели опубликовать проблему в репозитории – https://github.com/Azure/azure-quickstart-templates/issues/6932 – но самое главное в данной ситуации, что никому в Microsoft проблема со скриптом на GitHub для развертывания кластера в Azure не интересна – запрос висит уже месяц и только дополнился еще одним пострадавшим… Ну не работает у людей инфраструктура в Azure – проблемы индейцев пользователей шерифа Microsoft не волнуют, Azure не требует поддержки для пользователей по мнению самого Microsoft. Кстати, и запрос к техническому специалисту в Microsoft, который привязан к конкретному заказчику «пошуршать по продуктовой команде и найти тех, кто выправит то, что сделалось кривым» – так и ушел «вникуда»… Периодически пингую «специалиста» последнюю неделю без какого-либо успеха… А что, Рождество же, Azure и проблемы могут и подождать, «ручками развернете и сконфигурите кластеры»…

Заранее предполагая такую проблему с тем, что в Microsoft об восстановлении скрипта никто и «не почешится», решил сделать запасной вариант развертывания кластеров в Azure, интегрированный в ARM template и использующий «чистый» PowerShell – разбираться, что пошло не так со сценарием DSC в «оригинальном» решении Microsoft, времени и желания не было.

Потому, пока Microsoft не пофиксит (может быть) «главный скрипт», вот вам основная идея, как автоматически развертывать кластеры в Azure через ARM с zero-touch. Да, получилось не так универсально, как в оригинале, но оно простое и понятное для всех, легко модифицируется и, главное, работает 😉

Итак, основные технические нюансы и сам код:

Первое – в Azure можно поднять только Windows Failover Cluster в режиме Storage Spaces Direct на 3 узла максимум. Кроме того, не все нагрузки поддерживаются в таком режиме в Azure, и, самое главное – цена вопроса, поскольку требуются производительные премиумные SSD диски для работы дисковой системы в режиме репликации Storage Spaces Direct в таком кластере.

Второе – чтобы развернуть кластер, необходимо, чтобы виртуальные машины, в нем участвующие, как узлы, имели частные статические IP в виртуальной сети Azure и были добавлены в единый Availability Set. ОС внутри этих ВМ должны быть добавлены в домен (кстати, если нужно только добавить ВМ в домен, то это можно сделать и непосредственно в ARM через расширение JsonADDomainExtension), на них установлены необходимые роли и службы. Это не проблема, такую работу выполняет простой PowerShell скрипт:

[CmdletBinding()]

param (

    [Parameter()]

    [string]

$domainname,

    [string]

$biosname,

    [string]

$adminname,

    [string]

$adminpwd

)

#Online the cluster’s disks

Get-Disk |  Where-Object IsOffline -eq $true | Set-Disk -IsOffline $false

#install failover clustering services and tools

Install-WindowsFeature Failover-Clustering -IncludeAllSubFeature -IncludeManagementTools

Start-Sleep -Seconds 5

Install-WindowsFeature FS-FileServer

Start-Sleep -Seconds 5

# generate domain admin’s credentials for domain join

$adminusername = “$biosname\$adminname”

$password = ConvertTo-SecureString -String $adminpwd -AsPlainText -Force

$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $adminusername, $password

Add-Computer -DomainName $domainname -Credential $cred -Restart -Force

Скрипт помещается в виде файла в какой-то Azure BLOB Storage (не забудьте разрешить анонимный доступ к объектам внутри контейнеров, хотя можно и заморочиться с атрибутам ресурса и указанием ключа доступа к этому BLOB) и запускается такой скрипт внутри VM из ARM template, как ресурс для создаваемой виртуальной машины с использованием extentions типа CustomScriptExtention, с указанием правильного пути к скрипту, который вы только что скопировали в BLOB (атрибут fileUris) и генерацией правильной командной строки (атрибут commandToExecute). Вот пример фрагмента кода именно ресурса (должен быть пристыкован к BМ) для ARM template:

“resources”: [

            {

“type”: “extensions”,

“name”: “CustomScriptExtension”,

“apiVersion”: “2017-03-30”,

“location”: “[parameters(‘location’)]”,

“dependsOn”: [

“[concat(‘Microsoft.Compute/virtualMachines/’, parameters(‘vmname1av1’))]”,

“[concat(‘Microsoft.Compute/virtualMachines/’, parameters(‘vmname2av1’))]”

              ],

“properties”: {

“publisher”: “Microsoft.Compute”,

“type”: “CustomScriptExtension”,

“typeHandlerVersion”: “1.8”,

“autoUpgradeMinorVersion”: true,

“settings”: {

“fileUris”: [

https://mystorage.blob.core.windows.net/CL/pre-setupVM.ps1”

                  ],

“commandToExecute”: “[concat(‘powershell -ExecutionPolicy Unrestricted -File pre-setupVM.ps1 -domainname ‘, parameters(‘domainname’), ‘ -biosname ‘, parameters(‘netbiosname’), ‘ -adminname ‘, parameters(‘adminUsername’), ‘ -adminpwd ‘, parameters(‘adminPassword’))]”

}

}

}

]

Еще хочу обратить внимание на формирование строки для выполнения пользовательского скрипта – начинается она с вызова powershell с правильными параметрами -ExecutionPolicy Unrestricted и указанием имени файла вашего скрипта в параметре -File.

Данный скрип будет запущен после развертывания самой виртуальной машины ARM шаблоном и сконфигурирует диски, установит нужные роли и службы в виртуальную машину, добавит ее в домен и перезагрузит.

Третье – и вот тут, после выполнения скрипта и перезагрузки каждой машины начинаются проблемы. ARM считает свою работу по запуску скриптов выполненной и, самое главное, в описание каждой из виртуальной машины в ARM template вы можете добавить только один ресурс типа extensions/CustomScriptExtension. А после перезагрузки нам нужно запустить на одном из узлов будущего кластера еще один скрипт, который сформирует кластер. Сделать это в ARM template нельзя – второй скрипт не указывается, и объяснить ARM, что после перезагрузки на машине стартует еще один скрипт и окончания его работы тоже нужно дождаться – тоже не получится… Потому – занимаемся «лайфхакингом». Если нельзя сделать внутри ARM template, это можно сделать в скрипте PowerShell, который запускает развертывание ARM template. Схематически такой скрипт выглядит следующим образом:

  1. Запускаем команду PowerShell на развертывание ARM – New-AzResourceGroupDeployment с указанием файла «большого» ARM, который развернет всю требуемую инфраструктуру и запустит на требуемых виртуалках скрипт конфигурации машины (выше);
  2. А теперь «хак» – для каждой из виртуальных машин, где уже был выполнен скрипт, запускаем команду по удалению расширения, что позволит выполнить еще одно развертывание ARM со вторым скриптом, который и сконфигурирует кластер – Remove-AzAMCustomScriptExtention . Единственное НО тут – это то, что данная команда для каждой из ВМ выполняется достаточно долго – 3-5 минут.
  3. Снова запускаем New-AzResourceGroupDeployment – на этот раз для развертывания второго шаблона ARM, в котором для каждой второй виртуальной машины (одной из пары узлов кластера) выполняется другой скрипт, как extensions/CustomScriptExtension.

Четвертое – для работы кластера требуется кворум, который для нашего варианта в Azure реализуется с использованием Cloud Witness. Для этого требуется создать Azure BLOB Storage, получить Access Key этого хранилища и использовать ключ для конфигурирования кворума кластера в режиме Cloud Witness. Потому в логике скрипта выше появляется еще один пункт 2а, где после создания инфраструктуры и сервисов (в том числе и хранилища для Cloud Witness) и удаления первых расширений, мы получаем ключи доступа для созданных хранилищ и на следующем шаге передаем их как параметр в новый ARM template – и дальше уже внутри ARM они будут переданы в вызываемый через extensions/CustomScriptExtension скрипт PowerShell для создания непосредственно кластера.

Итого, общий код PowerShell скрипта, который будет выполнять развертывание двух ARM template, с переключением extensions/CustomScriptExtension и получением ключей хранилища:

# deploy basic infrastructure from main ARM template (defined in $templateFilePath)

New-AzResourceGroupDeployment -ResourceGroupName $resourceGroupName -Mode Incremental -TemplateFile $templateFilePath -TemplateParameterObject $Params | Out-Null

# delete CustomScriptExtension from deployed VMs (names should be defined in $deployedVMs array)

foreach($vmname in $deployedvms){

Remove-AZVMCustomScriptExtension -ResourceGroupName $ResourceGroupName -VMName $vmname -Name “CustomScriptExtension” -Force | Out-Null

}

# get storage account’s key for Cluster quorum Cloud Witness and set values in $Params object for next ARM deployment

$keys = Get-AzStorageAccountKey -ResourceGroupName $ResourceGroupName -Name $Params[‘storagename1’]

$Params[‘key1’] = $keys[0].Value

# deploy second CustomScriptExtension (build the cluster) from second ARM template (defined in $templateFilePath2)

New-AzResourceGroupDeployment -ResourceGroupName $resourceGroupName -Mode Incremental -TemplateFile $templateFilePath2 -TemplateParameterObject $Params | Out-Null

Теперь дело за малым – за созданием кластера.

Сам код вызова extensions/CustomScriptExtension во втором ARM template выглядит аналогично первой части (второй скрипт тоже не забудьте поместить в правильный контейнер, как и в первом случае), единственно – в параметрах скрипта передается 2 имени узлов будущего кластера и другие его параметры:

“resources”: [

        {

“type”: “Microsoft.Compute/virtualMachines/extensions”,

“name”: “[concat(parameters(‘vmName2av1′),’/customscript’)]”,

“apiVersion”: “2015-06-15”,

“location”: “[parameters(‘location’)]”,

“properties”: {

“publisher”: “Microsoft.Compute”,

“type”: “CustomScriptExtension”,

“typeHandlerVersion”: “1.4”,

“settings”: {

“fileUris”: [

https://mystorage.blob.core.windows.net/CL/setupCluster.ps1”

                    ],

“commandToExecute”: “[concat(‘powershell -ExecutionPolicy Unrestricted -File setupCluster.ps1 -domainname ‘, parameters(‘domainname’), ‘ -biosname ‘, parameters(‘netbiosname’), ‘ -adminname ‘, parameters(‘adminUsername’), ‘ -adminpwd ‘, parameters(‘adminPassword’), ‘ -vmnames \”‘, parameters(‘vmname1av1′),’,’,parameters(‘vmname2av1’),’\” -clustername ‘, parameters(‘availabilitysetname1’), ‘ -clusterip ‘, variables(‘cluster1’), ‘ -storageaccount ‘, parameters(‘storagename1’), ‘ -storageacckey ‘, parameters(‘key1’))]”

                }

            }

        }

]

Пятое – чтобы развернуть кластер, скрипт должен выполняться с правами администратора домена, а вот CustomScriptExtension исполняет скрипт с правами ОС внутри виртуальной машины. Но скрипт PowerShell нельзя имперсонализировать в самом скрипте, но, как вариант, можно выполнить команду с правами администратора на удаленном компьютере через Invoke-Command. Т.е. запускаем скрипт через ARM template, передаем логин/пароль админа в скрипт, который создает идентити админа и вызывает команду через Invoke-Command с правами администратора.

Шестое – Windows Failover Cluster НЕЛЬЗЯ развернуть удаленно через Invoke-Command даже с правами администратора. НО, можно сконфигурировать удаленное исполнение команд Invoke-Command через аутентификацию CredSSP, которую предварительно надо настроить на обоих узлах с использованием пары перекрестных команд Enable-WSManCredSSP:

  • Для каждого из узлов включаем режим сервера Enable-WSManCredSSP Server -Force
  • Для каждого из узлов включаем доверие Enable-WSManCredSSP Client -DelegateComputer <имя второго узла> -Force

После конфигурации CredSSP для каждого узла появляется возможность использования Invoke-Command для создания кластера.

Таким образом второй скрипт PowerShell, который создает кластер, будет выглядеть следующим образом:

[CmdletBinding()]

param (

    [Parameter()]

    [string]

$domainname,

    [string]

$biosname,

    [string]

$adminname,

    [string]

$adminpwd,

    [string]

$vmnames,

    [string]

$clustername,

    [string]

$clusterip,

    [string]

$storageaccount,

    [string]

$storageacckey

)

# create domain admin credential’s object

$adminusername = “$biosname\$adminname”

$password = ConvertTo-SecureString -String $adminpwd -AsPlainText -Force

$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $adminusername, $password

#get nodes name from parameter string like “node1,node2”

$nodesArr = $vmnames -split “,”

$n1 = $nodesArr[0]

$n2 = $nodesArr[1]

#enable CredSSP authentication for admin impersonation by remote calls

#set both nodes as CredSSP’s servers

Invoke-Command $nodesArr -Credential $cred {Enable-WSManCredSSP Server -Force}

#set current node CredSSP’s client that trusts other node

Enable-WSManCredSSP Client -DelegateComputer $n1 -Force

#set other node CredSSP’s client that trusts current node

Invoke-Command $n1 -Credential $cred {Enable-WSManCredSSP Client -DelegateComputer $Using:n2 -Force}

Start-Sleep -Seconds 5

# create cluster as domain admin with one node by remote call over CredSSP

Invoke-Command $n1 -Credential $cred {New-Cluster -Name $Using:clustername -Node $Using:n1 -NoStorage -AdministrativeAccessPoint ActiveDirectoryAndDns -StaticAddress $Using:clusterip -Force} -Authentication Credssp

Start-Sleep -Seconds 30

Invoke-Command $n1 -Credential $cred {Get-Cluster $Using:clustername} -Authentication Credssp

Start-Sleep -Seconds 5

# add second node to the cluster as domain admin by remote call over CredSSP

Invoke-Command $n1 -Credential $cred {Get-Cluster -Name $Using:clustername | Add-ClusterNode -Name $Using:n2 -NoStorage} -Authentication Credssp

Start-Sleep -Seconds 30

Invoke-Command $n1 -Credential $cred {Get-Cluster $Using:clustername} -Authentication Credssp

Start-Sleep -Seconds 5

# configure cluster’s Storage Spaces Direct as domain admin by remote call over CredSSP

Invoke-Command $n1 -Credential $cred {Enable-ClusterStorageSpacesDirect -Confirm:$false} -Authentication Credssp

Start-Sleep -Seconds 30

# configure cluster’s cloud witness as domain admin by remote call over CredSSP

Invoke-Command $n1 -Credential $cred {Get-Cluster -Name $Using:clustername | Set-ClusterQuorum -CloudWitness -AccountName $Using:storageaccount -AccessKey $Using:storageacckey} -Authentication Credssp

Start-Sleep -Seconds 5

# configure cluster’s cloud witness as domain admin by remote call over CredSSP

Invoke-Command $n1 -Credential $cred {Get-StoragePool S2D* | New-Volume -FriendlyName Data -FileSystem CSVFS_ReFS -UseMaximumSize} -Authentication Credssp

Вот и все готово… Получилось, как я уже писал выше – куда более лаконично, нежели нагромождение кода DSC у Microsoft (который и обвалился в результате), более понятно и читаемо (хотя да, есть некоторые «странности», но это опять таки – вопросы к МС с его Invoke-Command и ограничениями для extensions/CustomScriptExtension) и, главное – оно работает, в отличие от «элитарного» кода Microsoft.

Сейчас держим про запас 2 набора файлов IaC для наших заказчиков с кластерами в Azure – один с «классическим» и нерабочим сейчас кодом Microsoft для создания кластеров через ARM/DSC – в надежде, что Microsoft таки читает запросы на разрешение проблем и хоть как-то сопровождает свой код, и второй – вот данный момент рабочий и приведенный выше.

Я уже писал про компетенцию и поддержку своих продуктов со стороны Microsoft – этот случай из той же оперы, причем тут Microsoft явно забил на свой публично опубликованный код (типа, а вот сопровождать не обещали). Так что, как сказано в предисловии к посту – верить никому нельзя и мы таки попытались выправить то, что Microsoft сделал кривым.

И да, если Microsoft не в состоянии поддерживать документацию, код и вообще – оказывать качественный саппорт – может, им стоит прислушаться к тому совету, который мне написали в комментариях к предыдущему моему посту (хотя Microsoft пошел простым путем – находит тех, кто делает это бесплатно за непонятные и совершенно эфимерные плюшки статуса MVP, который еще и довольно таки “подозрителен”, как я уже писал ранее):

Игорь, привет. По поводу твоего свежего поста – совет: предложить им сделать аналог Bug Bounty/Marketplace: за чужой хороший код и документацию платить премии.

Друге посты и видео по теме Azure и ИТ-карьеры:

Microsoft Azure Functions на PowerShell–включение аутентификации пользователя для HTTP Trigger, получаем информацию об аутентификации пользователя в коде функции на PowerShell и про “поддержку” Microsoft.


Начнем с технической части повествования – чтобы народу было интересно и полезно почитать, а уже потом перейдем к «политическо-ИТшной» – это уже по желанию, но как по мне так смешно, что аж печально… 😉

Итак, в некоем решении используются Azure Functions, написанные на PowerShell Core (потому что все мои админы знакомы и легко могут модифицировать, развивать решение при необходимости) и в качестве безопасности (аутентификации пользователей) работающие со стандартной идентификацией от Azure AD при вызове того, что называется Http Trigger, т.е. функции, имеющей свой персональный URL, по которому ее и можно запустить, передав требуемые параметры.

Требуется – при развитии решения появилась потребность не только аутентифицировать пользователей, но и иметь некие аналоги аудита (кто какие функции вызывает) и ролевой модели (запрет некоторым людям вызывать функции или использовать глобальные параметры).

Аутентификация на базе Azure AD для Azure Functions включается в пару кликов и не требует какого-либо изменения кода самой Azure Functions – для функции вся аутентификация работает при описанном ниже сценарии «прозрачно», всю работу по аутентификации на себя берет «движок» Azure. Не путать, кстати, с опцией Identity, которая позволяет имперсонализировать сам исполняемый экземпляр Azure Functions в Azure и выполнять код под определенным аккаунтом в Azure, которому можно предоставлять разные права – чем, собственно, мы и пользуемся – используя Azure Functions для выполнения различных задач обслуживания, предоставляя толпе из L1 для выполнения базовых операций не доступ к порталу Azure и клиентским подпискам, а только к функциям, которые уже все сделают «как нужно и правильно»…

Но вернемся к аутентификации пользователей при вызове Azure Functions. Итак:

Заходим на основную страницу требуемой Azure Functions, переключаемся в закладку Platform Features, и там кликаем на опцию Authentication / Authorization

clip_image002

На открывшейся странице устанавливаем переключатель App Service Authentication в положение On и в появившемся ниже выпадающем списке Action to take when request is not authenticated выбираем опцию Log in with Azure Active Directory, а в следующем списке Authentication Providers кликаем на пункт Azure Active Directory / Not Configured

clip_image004

Откроется новое окно настроек – создания экземпляра приложения для аутентификации в Azure AD, где в Managed Mode выбираем опцию Express и далее, в появившихся ниже опциях, выбираем правильный каталог Azure AD (если в вашем профиле их несколько зарегистрировано), Managed Mode оставляем, как есть – Create New AD App – и оставляем имя по умолчанию.

clip_image006

Кликаем Ok в данном диалоге, ждем окончания процесса создания объекта, и в форме Authentication / Authorization, куда вас вернет портал, кликаем вверху на иконку Save. Опять таки – ждем окончания операции и получаем сконфигурированное приложение с аутентификацией от Azure AD. Теперь, при обращении к Azure Functions, которые зарегистрированы, как Http Trigger, Azure будет запрашивать у вызывающего пройти аутентификацию и предъявить аккаунт пользователя из указанной выше Azure Active Directory. Так что анонимные пользователи такую функцию теперь не вызовут.

НО! Такой сценарий достаточен только для «отсечения» анонимных пользователей, а вот если требуется какое-либо подобие RBAC внутри самих Azure Functions – например, пользователь аутентифицируется, но при этом часть параметров функции ему недоступна или доступ к данным внутри самой функции должен быть ограничен – здесь, увы, такая аутентификация не поможет. Да и вообще, у Azure нет толкового RBAC для Functions.

Т.е. после вызова функции вызов перенаправляется на аутентификацию в Azure AD и если все ок – управление передается на Azure Functions и нам надо теперь внутри исполняемой функции получить клейм пользователя и все атрибуты его учетной записи – ID в аутентифицирующей системе, имя, саму систему и прочие токены. При этом все это еще требуется получить в PowerShell.

И тут начинается самое интересное – хотя Microsoft со всех трибун кричит про внедрение Serverless решений, рассказывает про использование Azure Functions вместе с PowerShell для процессов автоматизации – в реальности никакой официальной документации от Microsoft, особенно фундаментальной, по теме Functions/PowerShell вы не найдете. В Интернете ее тоже нет, даже на любимом всеми StackOverflow – просто никто не использует решение, не смотря на громкие заявления Microsoft.

Нашел пару документов про работу с идентити/информацией аутентификации в Functions/Web Apps применительно к C#, но это нагромождение классов и вызовов было явно не релевантно для кода PowerShell. Чтобы понять всю глубину проблемы, стоит сказать, что ни я, ни моя команда – ни разу не девелоперы – только PowerShell скрипты для автоматизации процессов заказчиков и auto-healing, только хардкор, как, например, скриптик PowerShell на 1000 строк для правильного «развертывания всего» с соответствующими внутренностями VM. Для нас вообще удивительно, кстати, почему работа с тем же Azure Storage Queues в PowerShell пишется в 3 строки, а в C# сначала надо создать десяток классов при неочевидном выигрыше в производительности самой Functions. Но это уже лирика, а вывод простой – для людей, которые занимаются автоматизацией процессов на PowerShell, документации по разработке на сайте Microsoft совершенно недостаточно, приходится собирать по крупицам с разных источников, работать «методом тыка» или открыть тикет в саппорте. На том и порешили – продолжаем «копать» и обращаемся в саппорт.

Методом логического анализа кода («методом тыка») на C# обнаружилось, что чтобы работать cо всей информацией по аутентификации пользователя, вызывающего Functions с аутентификацией в Azure AD (как настроено выше) в PowerShell коде можно через Headers одной командой:

$Request.Headers

Ниже – представленный в JSON объект заголовка вызова Http Trigger в Azure Functions (тот самый $Request.Headers). Все поля, как на ладони и далее работаем с ними, как с обычным hashtable в PowerShell, т.е. получаем нужное значение по имени ключа (которым являются поля заголовка ниже).

clip_image008

Т.е., чтобы получить имя пользователя, его ID, его Access Token и т.п. в коде PowerShell – требуется максимум 1-2 команды:

$headersObj = $Request.Headers # get call’s headers

$username = $headersObj[“x-ms-client-principal-name”]   # get user account name

$userid = $headersObj[“x-ms-client-principal-id”]       # get user AAD ID

$userip = $headersObj[“x-forwarded-for”]                # get user IP

И все, далее используем переменные или обращаемся к нужному элементу по имени ключа. Все, тема закрыта, используем нужную информацию для журналирования и внутренних проверок.


И тут начинается вторая часть истории – которая смешная и печальная, особенно в разрезе предыдущей моей истории про то, как Microsoft Ukraine заблокировала и удалила мои видео по курсу для сертификации AZ-900 по Azureпродолжения истории c Microsoft Ukraine после).

Итак, открыл я кейс в Microsoft support по данной проблеме и вот выдержка переписки:

1. Я описываю проблему

clip_image010

2. И получаю от Microsoft Support ответ, что в моем сценарии (option 1, процесс конфигурирования которого и описан выше) такую информацию получить нельзя:

clip_image012

3. Причем, получив такой ответ – я уже 100% уверен в возможности получения этих данных, просто еще не имею на руках конкретного кода. И я отправляю письмо типа – «точно нельзя с опцией 1? Уверены?» и получаю от Microsoft support еще один отрицательный ответ – нет, вы не можете получить такую информацию (причем, происходит это через 5 дней, три из которых – рабочие):

clip_image014

4. И в ответ на это дело я уже пишу, что нашел сценарий, код из 2х строк работает и можно закрывать кейс.

И вот тут начинается самое интересное и «смешное». Мне сразу же перезванивает (буквально в течение 15 минут, до этого отвечал только по почте и то – через сутки) сотрудник службы поддержки Microsoft, который просит поделиться с ними решением проблемы и кодом!!! Т.е. Microsoft, который до этого активно удалял мои технические видео про Azure из принадлежащей мне группы в Facebook, а потом и удалил мой аккаунт из админов – про это читайте тут и тут – теперь вдруг другой рукой просит поделиться информацией со своей поддержкой?! Серьезно?! Я должен бесплатно делать за сотрудников Microsoft их работу? Той информацией, которую Microsoft в качестве базовой просто обязан опубликовать в документации, поскольку и продукт – платный, и везде, со всяких трибун, Microsoft кричит о важности безопасности вообще и правильного кода – в частности. И уж точно этой информацией должна владеть служба поддержки Microsoft…

Чтобы вам было еще смешнее – я и сейчас продолжаю получать письма с просьбами отдать код, последнее пришло уже сегодня, 25 декабря, через 9 дней после того, как я сообщил, что задача имеет решение. Вот реально?! За 9 дней внутри Microsoft собственный отдел поддержки не смог найти техническое решение и продолжает ждать у моря погоды – поделюсь ли я кодом?!

clip_image016

И самое смешное – Microsoft за последние 3-5 лет сократил практически весь высококвалифицированный технический персонал из своих локальных офисов ввиду того, что Azure поддержки не требует, его надо только продавать, а технической поддержкой пусть занимаются партнеры «за деньги». Azure, как показывает опыт, просто за бла-бла-бла девочек из маркетинга Microsoft не продается (про это я говорю тоже в первой серии курса AZ-900), а партнеры, при таких отношении, «экспертизе», «документации» и «поддержке» от Microsoft – точно не будут вкладываться в «прибыльный и простой бизнес Azure» и не будут развивать компетенции Azure, как и клиенты не горят желанием платить деньги «каким-то партнерам», один раз столкнувшись с подобным невежеством поддержки «самого Microsoft». И, особенно, партнерам не интересно это делать, если развитие компетенций не интересно самому Microsoft и даже поддержка Microsoft не в состоянии дать ответ на простой, как оказалось, вопрос.

И если объективно смотреть на ситуацию c поддержкой Azure, то можно смело сказать, исходя из своего опыта работы С Microsoft последние пару лет, что никакой реальной поддержки и технической компетенции «на местах» у Microsoft сейчас просто нет. Выгребайте сами. И иметь у себя классного специалиста по Azure в штате – это будет даже лучше и безопаснее, чем вам пришлют какого-то «технического консультанта» от Microsoft, который в реальности окажется «не пойми кто», как я уже писал во второй части большого поста про Microsoft.

Но и это еще не все про Microsoft и поддержку Azure… Читайте в следующем посте про развертывание Windows кластеров в Azure и про официальные скрипты от Microsoft.

Друге видео то теме Azure и ИТ-карьеры: