Management Zertifikate per PowerShell

Grade bin ich darüber gestolpert, wie man Management-Zertifikate nach Azure hochladen kann, ohne die GUI, also Webportal, zu nutzen. Da dachte ich, schreib das mal hier rein, vielleicht kann es ja noch jemand brauchen, zum Beispiel die Preview Kunden der Microsoft Cloud für Deutschland…

Voraussetzung hierfür ist, dass man hat bereits ein Management-Zertifikat besitzt, zum Beispiel weil man (in der Azure Cloud) den Mechanismus mit dem publishsettings-File genutzt hat oder sonst irgendwie. Jetzt möchte man ein weiteres Zertifikat nutzen oder das alte ersetzen. Wie geht das?

Eigenes Zertifikat erzeugen

Zuerst erzeugen wir uns mal ein eigenes, selbst unterschriebenes Zertifikat, und das ist schon gar nicht so einfach, obwohl es mehrere Wege gibt:

  1. Wir haben schon ein persönliches Zertifikat, das wir dafür verwenden wollen. Prima.
  2. Wir nutzen makecert.exe. Das ist ein kleines Programm, das zum Beispiel mit Visual Studio ausgeliefert wird, oder an anderer Stelle zum Download liegt (natürlich nur vertrauenswürdige Sites wie zum Beispiel microsoft.com verwenden!).
  3. uns fällt noch eine andere Lösung ein (zum Beispiel mit openssl unter Linux)

Wir machen das mal mit makecert. Die komplette Zeile lautet:

makecert.exe –sky exchange –r –n “CN=AzureMgmt” –pe –a sha1 –len 2048 –ss My azuremgmt.cer

Ich will nur ein paar der Parameter erklären: “AzureMgmt” ist der Name, unter dem wir das nachher wiederfinden. Man kann natürlich hier irgendeinen Namen angeben, dann muss man halt die Anleitung weiter unten entsprechend ändern. “-ss My” legt das Zertifikat unter “Eigene Zertifikate” ab, auch das dient der leichteren Auffindbarkeit und entspricht dem Standard. Die entstehende Datei (“azuremgmt.cer”) enthält nur den öffentlichen Teil der Zertifikats, das ist der Teil, den wir sonst über das Portal hochladen würden. Für uns hier brauchen wir die nicht weiter, das geht einfacher. Alle Infos, die wir brauchen, sind auch im Zertifikatsspeicher drin, und wir holen uns die direkt von dort.

REST-Schnittstelle nutzen

Um Zertifikate hochladen zu können, verwenden wir die sog. REST-Schnittstelle. Das sind im Prinzip Web-Anfragen, ähnlich wie sie ein Browser sendet, wenn wir eine Seite anschauen wollen. In PowerShell verwenden wir dafür das Cmdlet “Invoke-WebRequest” und geben – je nach gewünschter Funktion – eine entsprechende Adresse und ein sog. Methode an. Die Methode ist beim normalen browsen meist “GET”, bei Formularen auch mal “POST”, aber es gibt auch noch mehr, und ein, zwei werden wir gleich kennen lernen. Für die Webanfrage brauchen wir eine Adresse, und die unterscheidet sich, je nachdem ob man in der normalen Azure Cloud oder in der Microsoft Cloud Deutschland arbeitet. Die Adresse steckt im jeweiligen Azure Environment, genauer gesagt in der ServiceManagementUrl:

$baseurl=(Get-AzureEnvironment -Name (Get-AzureSubscription -Current).Environment).ServiceManagementUrl

Als Teil der Adresse übergeben wir unsere Subscription-ID, die können wir entweder über

Get-AzureSubscription

angezeigt bekommen und dann mit copy&paste rauskopieren, oder (wir sind ja elegant) aus der aktiven Subscription auch direkt auslesen:

$subID = (Get-AzureSubscription -Current).subscriptionID

Dann müssen wir dem Aufruf noch eine Authentifizierung mitgeben.

Authentifizierung mit Zertifikat

Eigentlich ganz einfach, wir brauchen nur die Zertifikatsdatei mit dem privaten Schlüssel. Je nachdem, wo wir unser Zertifikat her hatten, liegt die Datei vielleicht noch irgendwo (also hoffentlich an einer halbwegs sicheren Stelle). Falls nicht, müssen wir das erst exportieren, ist aber auch nicht kompliziert. Einfach den Zertifikatsmanager starten, dann unter “Eigene Zertifikate” das gewünschte Zertifikat suchen, Rechtsklick , Alle Aufgaben, Exportieren. Den privaten Schlüssel mit exportieren, ein Passwort (bitte merken), und ein Pfad (auch merken) angeben. Fertig. Das Zertifikat lesen wir dann in eine Variable ein, die wir beim späteren Webaufruf mitgeben:

$authcert=Get-PfxCertificate -FilePath $path_to_valid_cert

Da müssen wir dann noch das Passwort, dass wir beim Exportieren angegeben haben, angeben, und anschließend ist unser Zertifikat in der Variable gespeichert.

Ein erster Kontakt mit REST

$answer=Invoke-WebRequest –uri $baseurl/$subid/certificates
-Method GET -Headers @{"x-ms-version"="2012-03-01"} -Certificate $authcert

Keine Panik! Ganz ruhig! Sieht schlimmer aus, als es ist. Wir schauen uns das Stück für Stück an. Zuerst die URI (das ist die Webadresse): Die setzt sich zusammen aus der Serveradresse, direkt gefolgt von der Subscription-ID (deswegen haben wir die vorhin ermittelt und gespeichert) und einem Unterbereich (“certificates”). Schon mal gar nicht so schwer, oder? Weiter geht’s. Die Methode, die wir hier verwenden, ist “GET”, wir wollen etwas als Antwort haben. Das mit den Headern muss man nicht groß erklären, die müssen einfach sein. Ja, und dann kommt am Ende noch unsere Authentifizierung (schließlich soll ja nicht jeder zugreifen können), und auch das haben wir ja weiter oben erledigt und abgespeichert. Das war’s auch schon.

Wir haben jetzt eine Antwort, und die schauen wir uns mal genauer an:

$answer

zeigt den Inhalt, und wir sehen ganz viel. Zum Beispiel einen StatusCode, irgendwo in den 200ern. Und wir sehen Content. Und in diesem Content steckt eine Liste aller unserer schon früher hochgeladener Zertifikate.

$answer.Content

liefert das ausführlicher. Aber auch nicht gerade besser lesbar. Wir können das – falls es jemanden interessiert – einfach speichern und mit einem XML-Viewer, zum Beispiel IE, anschauen:

$answer.content | out-file c:\temp\answer.xml
c:\temp\answer.xml

Upload formatieren

Jetzt wissen wir also, wie wir eine Liste unserer Zertifikate abrufen. Eigentlich wollten wir ja aber ein neues hinzufügen. Und das lesen wir uns jetzt erst mal aus:

$name_of_new_cert="AzureMgmt"
$certToUpload=get-childitem Cert:\CurrentUser\My |
Where-Object {$_.Subject -match $name_of_new_cert}

Wir brauchen diverse Teile dieses Zertifikats, die wir uns mit den folgenden Zeilen besorgen:

$publicKey=[System.Convert]::ToBase64String($certToUpload.GetPublicKey())
$thumbprint = $certToUpload.Thumbprint
$certificateData = [System.Convert]::ToBase64String($certToUpload.RawData)

Das Zertifikat, dass wir hochladen wollen, muss in einem XML-Format ähnlich dem vorhin runtergeladenen formatiert werden. Hier kann die geniale PowerShell ihre Vorzüge voll ausspielen. Wir definieren einfach eine XML-Variable mit den folgenden Zeilen

$xmlframe=@'
<?xml version="1.0" ?>
<SubscriptionCertificate xmlns="https://schemas.microsoft.com/windowsazure">
<SubscriptionCertificatePublicKey></SubscriptionCertificatePublicKey>
<SubscriptionCertificateThumbprint></SubscriptionCertificateThumbprint>
<SubscriptionCertificateData></SubscriptionCertificateData>
</SubscriptionCertificate>

'@

$xml=[xml]$xmlframe

Das mit dem @’ ist der sog. Here-Operator, der auch in anderen Programmiersprachen vorkommt und einfach eine mehrzeilige Eingabe an dieser Stelle (“here”) erlaubt. Zuerst füllen wir eine normale Zeichenkette (xmlframe), und dann übertragen wir das in eine neue Variable, wobei wir das Format dieser neuen Variable mit Hilfe der eckigen Klammern explizit als xml angeben. Warum das? Nun, auf XML-Variablen kann man gezielt auf die einzelnen Bereiche zugreifen, und genau das machen wir, um die geforderten Parameter einzufügen:

$xml.SubscriptionCertificate.SubscriptionCertificatePublicKey = $publicKey
$xml.SubscriptionCertificate.SubscriptionCertificateThumbprint = $thumbprint
$xml.SubscriptionCertificate.SubscriptionCertificateData = $certificateData

Man fügt also einfach die einzelnen Knoten der XML-Variablen als Attributen mit eine Punkt getrennt an. Cool, oder? Wir haben jetzt alles zusammen für den Upload.

Zertifikat hochladen

Invoke-WebRequest –uri $baseurl/$subID/certificates
-Method Post -Headers @{"x-ms-version"="2012-03-01"} -Certificate $authcert
-Body $xml.outerxml -ContentType "application/xml"

Ein genauerer Blick auf die Teile: Wir verwenden hier die POST-Methode, wir wollen ja etwas hochladen. Den Teil zum Hochladen schicken wir im Body der Anfrage mit, und zwar unsere XML-Datei. Wenn alles gut ging, dann kommt ein Status “201” zurück (“Created”) und das neue Zertifikat kann zur Authentifizierung verwendet werden.

Zertifikat löschen

Ein neues Zertifikat hochgeladen? Das alte kann gelöscht werden? Kein Problem. Alles, was wir brauchen, ist der Thumbprint des Zertifikats, das wir löschen wollen. Wir holen uns also die Liste der Zertifikate (oder haben sie noch von oben) und wählen den Thumbprint aus. Dann verwenden wir als Methode “DELETE” (Überraschung!), und unser Kommando sieht so aus:

Invoke-WebRequest –uri $baseurl/$subID/certificates/$thumbprint
-Method Delete -Headers @{"x-ms-version"="2012-03-01"} -Certificate $authcert

 

Damit können wir jetzt Zertifikate auflisten, hinzufügen und löschen. Was will man mehr? Der Code ist übrigens auch auf Github verfügbar…