Ob mein Code gerade in der Produktionsumgebung oder in der Stagingumgebung der Windows Azure Cloud Services ausgeführt wird, ist nur ein Beispiel von Fragen, die mit den im SDK mitgelieferten Klassen nicht so einfach zu beantworten ist.
Abhilfe schafft dabei das Windows Azure Service Management REST API.
Wie man dieses API aus einer Web- oder Worker-Rolle heraus aufrufen kann, ist Thema dieses Blog Posts.
Vor einigen Monaten wurde mir die oben genannte Frage zu den Cloud Services gestellt.
Meine spontane Antwort darauf war:
Warum willst Du das im Code lösen?
Bau Dir doch entsprechende "Schalter" in die Cloud-Konfigurationsdateien ein.
Dennoch mag es mit Sicherheit Situationen geben, wo dies auch nicht eine adäquate Lösung darstellt.
Deshalb möchte ich anhand dieses Beispiels aufzeigen, wie man das Windows Azure Service Management REST API aus einer Web- oder Worker-Rolle heraus aufrufen kann …
Windows Azure Service Management REST API
Das Service Management API stellt eine Programmierschnittstelle zur Verfügung, die die meisten Funktionen des Windows Azure Management Portals abdeckt.
Um also die Frage nach der aktuellen Umgebung beantworten zu können, müssen wir einen Blick auf die Operationen für die Cloud Services werfen – genauer gesagt auf die Get Cloud Service Properties Operation.
Doch dazu später mehr…
Authentifizierung / Autorisierung
Um das Service Management API verwenden zu können, muss man sich verständlicher Weise vorher autorisiert haben.
Dies wird über selbstsignierte Zertifikate realisiert, deren öffentlicher Schlüsselteil zuvor im Windows Azure Management Portal hochgeladen werden muss.
Ein solches Zertifikat kann u.a. über die (mit Administratorrechten gestarteten) Visual Studio Kommandozeilen Tools erstellt werden:
makecert -sky exchange -r -n "CN=Windows Azure Tools" -pe -a sha1 -len 2048 -ss My "ProductionOrStagingDemo.cer"
Die dabei erstellte Datei muss – wie erwähnt – über das Management Portal (im Bereich Settings >> Management Certificates) hochgeladen und dabei einem Windows Azure Abonnement zugewiesen werden:
Dort wird auch anschließend die Subscription ID angezeigt, die wir später noch brauchen werden.
Das API in den Cloud Services nutzen
Um die Nutzung des Service Management APIs aus einer Web Rolle heraus zu demonstrieren, habe ich das Beispielprojekt "ProductionOrStagingDemo.zip (3,60 mb)" diesem Artikel beigefügt.
Dieses muss allerdings vorher entsprechen konfiguriert werden …
Das Zertifikat für die Rollen
Wenn man das Service Management API aus einer Web- oder Worker-Rolle heraus nutzen möchte, muss diese Rolle auch mit dem privaten Schlüssel des Zertifikats ausgestattet werden.
Dafür muss als erstes das Zertifikat (inkl. Privaten Schlüssel) als pfx-Datei exportiert werden.
Dazu startet man am Besten die MMC Konsole und fügt das Snap-In für Zertifikate hinzu.
Die Management Zertifikate befinden sich im Bereich My user account > Personal > Certificates und können via Export Wizard als pfx-Datei gespeichert werden.
Anschließend wechselt man in das Windows Azure Management Portal, in den Bereich Certificates des entsprechenden Windows Azure Cloud Services, um die pfx-Datei hochzuladen:
Der angezeigte Thumbprint des Zertifikats wird im Beispielprojekt benötigt:
Konfigurieren der Rolle
Als Nächstes ist die Konfiguration der Rolle dran …
Die URL der Get Cloud Service Properties hat folgendes Muster:
https://management.core.windows.net/<subscription-id> /services/hostedservices/<cloudservice-name>
Deshalb habe ich für die benötigten Informationen (Subscription ID und Cloud Service Name) zwei Einträge der Cloud-Konfiguration hinzugefügt.
Außerdem möchte ich später das Zertifikat mittels Thumbprint laden können, weshalb ein dritter Eintrag hinzu kam:
Zu guter Letzt habe ich das Zertifikat in der Cloud-Konfiguration registriert, damit dieses später in den Rollen installiert wird:
Der Abfrage des Service Management APIs
Nach der ganzen Konfiguration, jetzt endlich zum Code …
Für die Abfrage des Service Management APIs, habe ich im Beispielprojekt eine Klasse mit dem Namen ServiceManagement angelegt und dieser die Methode CreateHttpWebRequest hinzugefügt.
Mit dieser wird ein HttpWebRequest-Objekt erzeugt, welches bereits den benötigten Header (x-ms-version), den Content Type (application/xml) und das Client-Zertifikat enthält:
private static HttpWebRequest CreateHttpWebRequest(Uri uri, String httpWebRequestMethod, string managementCertificateThumbprint) { var x509Certificate2 = GetX509Certificate2(managementCertificateThumbprint); var httpWebRequest = (HttpWebRequest)WebRequest.Create(uri); httpWebRequest.Method = httpWebRequestMethod; httpWebRequest.Headers.Add("x-ms-version", "2009-10-01"); httpWebRequest.ClientCertificates.Add(x509Certificate2); httpWebRequest.ContentType = "application/xml"; return httpWebRequest; }
Um das Zertifikat zu laden, kam die Methode GetX509Certificate2 hinzu:
private static X509Certificate2 GetX509Certificate2(String thumbprint) { X509Certificate2 certificate; var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); try { store.Open(OpenFlags.ReadOnly); var certCollection = store.Certificates.Find( X509FindType.FindByThumbprint, thumbprint, false); if (certCollection.Count == 0) throw new Exception("No certificate found containing thumbprint " + thumbprint); certificate = certCollection[0]; } finally { store.Close(); } return certificate; }
Sowie natürlich die GetCloudServiceProperties-Methode.
Diese baut den entsprechenden HttpWebRequest zusammen und führt diesen aus.
Die Antwort wird in ein XDocument-Objekt geladen, damit diese später mit Linq2Xml abgefragt werden kann:
private static XDocument GetCloudServiceProperties(Guid subscriptionId, string cloudServiceName, string managementCertificateThumbprint) { XDocument responseDocument; var uri = new Uri(String.Format( "https://management.core.windows.net/{0}/services/hostedservices/{1}?embed-detail=true", subscriptionId, cloudServiceName)); var request = CreateHttpWebRequest(uri, "GET", managementCertificateThumbprint); using (var response = request.GetResponse()) { using (var responseStream = response.GetResponseStream()) { if (responseStream == null) throw new Exception("Server did not respond."); using (var readStream = new StreamReader(responseStream, Encoding.UTF8)) { responseDocument = XDocument.Load(readStream); readStream.Close(); } responseStream.Close(); } response.Close(); } return responseDocument; }
Mit der GetDeploymentSlot-Methode ziehe ich letztendlich das gewünschte Element aus dem XML, dass die Werte Production oder Staging enthalten kann.
Da die Get Cloud Service Properties-Operation für beide Umgebungen Werte zurückliefert, muss noch mittels Deployment ID bzw. Private ID gefiltert werden:
public static string GetDeploymentSlot( Guid subscriptionId, string cloudServiceName, string managementCertificateThumbprint, string deploymentId) { var cloudServiceProperties = GetCloudServiceProperties( subscriptionId, cloudServiceName, managementCertificateThumbprint); XNamespace aw = "http://schemas.microsoft.com/windowsazure"; var deploymentSlot = cloudServiceProperties .Descendants(aw + "Deployment") .Select(d => new { id = d.Element(aw + "PrivateID"), slot = d.Element(aw + "DeploymentSlot") }) .Where(d => d.id != null && d.id.Value == deploymentId) .Select(d => d.slot) .FirstOrDefault(); return deploymentSlot != null ? deploymentSlot.Value : "Unknown"; }
Im Controller der ASP.NET MVC Web-Rolle wird diese Methode dann entsprechend ausgeführt, um den Deployment Slot-Wert anzuzeigen:
public ActionResult Index() { try { var subscriptionId = CloudConfigurationManager.GetSetting("SubscriptionId"); var cloudServiceName = CloudConfigurationManager.GetSetting("CloudServiceName"); var mgmtCertThumbprint = CloudConfigurationManager.GetSetting("MgmtCertThumbprint"); if (RoleEnvironment.IsEmulated) ViewBag.EnvironmentType = "Development"; else ViewBag.EnvironmentType = ServiceManagement.GetDeploymentSlot( new Guid(subscriptionId), cloudServiceName, mgmtCertThumbprint, RoleEnvironment.DeploymentId); } catch (Exception ex) { ViewBag.ErrorMessage = ex.Message; } return View(); }
Download der Beispielanwendung:
Weitere Informationen
Verwendete Bildquellen:
© Barbara Eckholdt / PIXELIO
Freut mich, dass ich Dir damals weiterhelfen konnte!