Я пытаюсь создать управляемый кэш Azure с помощью PowerShell и службы управления Azure. API, этот двухсторонний подход необходим, поскольку официальные командлеты Azure PowerShell имеют очень ограниченную поддержку Создание и обновление управляемого кэша Azure. Однако существует ссылка установленный шаблон для вызова API управления Azure из PowerShell.
Моим попыткам найти правильный API для вызова несколько помешал ограниченная документация по API управляемого кэша Azure. Однако, проработав командлеты с использованием как исходного кода, так и параметра -Debug
в PowerShell, я смог найти то, что кажется правильными конечными точками API, поэтому я разработал некоторый код для доступа к этим конечным точкам.
Однако я застрял после того, как запрос PUT был принят в API Azure, поскольку последующие вызовы к конечной точке API/операций управления показывают, что результатом этой операции был Internal Server Error
.
Я использовал Joseph Alabarhari LinqPad для изучения API, поскольку он позволяет мне быстро перебирать решение, используя минимум возможный код, поэтому для выполнения следующих фрагментов кода вам потребуются как LinqPad, так и следующее расширение в скрипте "Мои расширения" :
public static X509Certificate2 GetCertificate(this StoreLocation storeLocation, string thumbprint) {
var certificateStore = new X509Store(StoreName.My, storeLocation);
certificateStore.Open(OpenFlags.ReadOnly);
var certificates = certificateStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
return certificates[0];
}
Полный исходный код, включая включения, доступен ниже:
- Мои расширения — вы можете заменить "Мои расширения" щелкнув правой кнопкой мыши «Мои расширения» в нижней левой панели и выбрав «Открыть расположение сценария в проводнике Windows», а затем заменив выделенный файл этим. В качестве альтернативы вы можете объединить мои расширения со своими.
- Сценарий управляемого кэша Azure — вы должны просто иметь возможность загрузите и дважды щелкните по нему, после открытия и установки вышеуказанных расширений и сертификатов вы сможете выполнить скрипт.
В сценарии используются следующие параметры. Следующие переменные потребуются всем, кто использует собственный идентификатор подписки Azure и сертификат управления:
var cacheName = "amc551aee";
var subscriptionId = "{{YOUR_SUBSCRIPTION_ID}}";
var certThumbprint = "{{YOUR_MANAGEMENT_CERTIFICATE_THUMBPRINT}}";
var endpoint = "management.core.windows.net";
var putPayloadXml = @"{{PATH_TO_PUT_PAYLOAD}}\cloudService.xml"
Сначала я выполнил некоторые настройки на HttpClient:
var handler = new WebRequestHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ClientCertificates.Add(StoreLocation.CurrentUser.GetCertificate(certThumbprint));
var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("x-ms-version", "2012-08-01");
Это настраивает HttpClient для использования как сертификата клиента, так и заголовка x-ms-version
, первый вызов API извлекает существующую службу CloudService, содержащую управляемый кэш Azure. Обратите внимание, что в противном случае используется пустая подписка Azure.
var getResult = client.GetAsync("https://" + endpoint + "/" + subscriptionId + "/CloudServices");
getResult.Result.Dump("GET " + getResult.Result.RequestMessage.RequestUri);
Этот запрос выполнен успешно, поскольку он возвращает StatusCode: 200, ReasonPhrase: 'OK'
, затем я анализирую некоторую ключевую информацию из запроса: имя CloudService, имя кеша и ETag кеша:
var cacheDataReader = new XmlTextReader(getResult.Result.Content.ReadAsStreamAsync().Result);
var cacheData = XDocument.Load(cacheDataReader);
var ns = cacheData.Root.GetDefaultNamespace();
var nsManager = new XmlNamespaceManager(cacheDataReader.NameTable);
nsManager.AddNamespace("wa", "http://schemas.microsoft.com/windowsazure");
var cloudServices = cacheData.Root.Elements(ns + "CloudService");
var serviceName = String.Empty;
var ETag = String.Empty;
foreach (var cloudService in cloudServices) {
if (cloudService.XPathSelectElements("//wa:CloudService/wa:Resources/wa:Resource/wa:Name", nsManager).Select(x => x.Value).Contains(cacheName)) {
serviceName = cloudService.XPathSelectElement("//wa:CloudService/wa:Name", nsManager).Value;
ETag = cloudService.XPathSelectElement("//wa:CloudService/wa:Resources/wa:Resource/wa:ETag", nsManager).Value;
}
}
Я предварительно создал файл XML, содержащий полезную нагрузку следующего запроса PUT:
<Resource xmlns="http://schemas.microsoft.com/windowsazure">
<IntrinsicSettings>
<CacheServiceInput xmlns="">
<SkuType>Standard</SkuType>
<Location>North Europe</Location>
<SkuCount>1</SkuCount>
<ServiceVersion>1.3.0</ServiceVersion>
<ObjectSizeInBytes>1024</ObjectSizeInBytes>
<NamedCaches>
<NamedCache>
<CacheName>default</CacheName>
<NotificationsEnabled>false</NotificationsEnabled>
<HighAvailabilityEnabled>false</HighAvailabilityEnabled>
<EvictionPolicy>LeastRecentlyUsed</EvictionPolicy>
</NamedCache>
<NamedCache>
<CacheName>richard</CacheName>
<NotificationsEnabled>true</NotificationsEnabled>
<HighAvailabilityEnabled>true</HighAvailabilityEnabled>
<EvictionPolicy>LeastRecentlyUsed</EvictionPolicy>
</NamedCache>
</NamedCaches>
</CacheServiceInput>
</IntrinsicSettings>
</Resource>
Я создаю HttpRequestMessage с указанной выше полезной нагрузкой и URL-адресом, состоящим из имен CloudService и Cache:
var resourceUrl = "https://" + endpoint + "/" + subscriptionId + "/cloudservices/" + serviceName + "/resources/cacheservice/Caching/" + cacheName;
var data = File.ReadAllText(putPayloadXml);
XDocument.Parse(data).Dump("Payload");
var message = new HttpRequestMessage(HttpMethod.Put, resourceUrl);
message.Headers.TryAddWithoutValidation("If-Match", ETag);
message.Content = new StringContent(data, Encoding.UTF8, "application/xml");
var putResult = client.SendAsync(message);
putResult.Result.Dump("PUT " + putResult.Result.RequestMessage.RequestUri);
putResult.Result.Content.ReadAsStringAsync().Result.Dump("Content " + putResult.Result.RequestMessage.RequestUri);
Этот запрос номинально принимается API управления службами Azure, поскольку он возвращает ответ StatusCode: 202, ReasonPhrase: 'Accepted'
; по сути это означает, что полезная нагрузка была принята и будет обработана в автономном режиме, идентификатор операции может быть проанализирован из заголовка HTTP для получения дополнительной информации:
var requestId = putResult.Result.Headers.GetValues("x-ms-request-id").FirstOrDefault();
Этот requestId
можно использовать для запроса обновления статуса операции:
var operation = client.GetAsync("https://" + endpoint + "/" + subscriptionId + "/operations/" + requestId);
operation.Result.Dump(requestId);
XDocument.Load(operation.Result.Content.ReadAsStreamAsync().Result).Dump("Operation " + requestId);
Запрос к конечной точке /operations приводит к следующей полезной нагрузке:
<Operation xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ID>5364614d-4d82-0f14-be41-175b3b85b480</ID>
<Status>Failed</Status>
<HttpStatusCode>500</HttpStatusCode>
<Error>
<Code>InternalError</Code>
<Message>The server encountered an internal error. Please retry the request.</Message>
</Error>
</Operation>
И здесь я застрял, скорее всего, я слегка искажаю запрос таким образом, что базовый запрос выдает 500 Internal Server Error, однако без более подробного сообщения об ошибке или документации API я не думаю, что есть везде я могу пойти с этим.