Kopieren von VHD Dateien zwischen verschiedenen Azure Installationen

Kürzlich hatte ich die Anforderung, zwischen zwei verschiedenen Azureinstallationen VHD Dateien kopieren zu müssen. Dabei hatte ich im ersten Mandanten (Quelle) die VHDs erstellt und in virtuellen Maschinen genutzt. In einer zweiten Installation (Ziel) von Azure wurde diese dann produktiv verwendet. Das Kopieren der VHDs kann ich natürlich realisieren, indem ich die Quell-VHD auf meinen Rechner herunterlade und dann wieder auf das Zielsystem hoch lade. Besser wäre allerdings die Möglichkeit, die VHDs direkt in Azure zu kopieren. Genau dies soll das unten stehende Skript bewirken. Das Skript können Sie auch von http://www.nicothiemer.de/Download/CopyVHDsInAzure.zip herunterladen.

Bei der Ausführung des Skripts gilt die Voraussetzung, dass das Zielsystem in Azure noch nicht konfiguriert ist. Der Azuremandat steht unmittelbar nach dem anlegen zur Verfügung. Da ich mehrere Mandanten habe, muss ich in der PowerShell zwischen diesen wechseln. Wie dies geht, beschreibt dieser Blogbeitrag https://nicothiemer.wordpress.com/2015/08/24/arbeiten-mit-mehreren-mandaten-in-der-azure-powershell. Gestartet werden sollte das Skript aus dem Mandanten des Zielsystems! Dieser muss auch als Default subscription eingetragen sein.Damit das Skript erfolgreich laufen kann, muss ich daher in der PowerShell mit dem Zielmandanten verbunden sein.

Was genau das Skript wie macht, ist in den Kommentaren beschrieben. Natürlich müssen die Dummys für die Variablen mit den echten Werten ersetzt werden. Einfach gesagt kopiert es eine VHD Datei und erstellt anschließend eine VM, welche die VHD nutzt. Es soll auch bewusst als Inspiration dienen. Ich habe darum auf Abfragen, ob schon Objekte bestehen, verzichtet. Damit sollte der eigentliche Code besser lesbar sein.

Anpassungen

Sie müssen sämtliche Variablen mit Ihnen eigenen Werten versehen. Einige Werte benötigen dazu besondere Aufmerksamkeit.

In der Zeile 55 muss noch eine Anpassung vorgenommen werden. In dieser wird mit Set-AzureVNetConfig das neu erstellte Netzwerk konfiguriert. Die XML Datei dazu steht in der Zeile 14 in der Variable $netconfigpath. Neben diesem Pfad müssen Sie außerdem noch die Datei als solche anpassen. Sie finden diese Datei in der .zip Datei zum Download oder am Ende des Artikels.

Was ich noch brauche, ist die URL der VHD, welche kopiert werden soll. Diese kann ich u.a. erhalten, wenn ich mir in der UI von Azure die Eigenschaften der VHD anzeigen lasse. Gehen Sie dazu im Quellsystem im (alten) Azureportal auf ihre virtuellen Rechner und klicken Sie dort auf den Reiter Datenträger. Ganz am rechten Rand haben Sie die Spalte Speicherort. Wenn Sie über diese Einträge mit der Maus fahren, erscheint ein Symbol zum Kopieren. Klicken Sie auf dieses und Sie haben den Speicherort der VHD. Die Variable für den Wert ist $diskSource und steht in Zeile 20.

Natürlich muss die virtuelle Maschine, welche die VHD die Sie kopieren möchten verwendet, heruntergefahren sein. Ich möchte auch nicht verschweigen, dass ich mich bei der Erstellung dieses Skripts großzügig vom Installationsskript für den Kurs MOC 20-346C Managing Office 365 Identities and Services inspiriert haben lassen.

Skript CopyVHDsInAzure.ps1


#Azure variables:

$affinitygroup = "Your affinitygroup"

#locations: https://azure.microsoft.com/en-us/regions/#services
$location = "West Europe"

#Storage
#only use lowercase letters and numbers!
$storage = "your storagename"

#network variables
$dnsname = "Your dns"
$netconfigpath ="C:\Azure\NetworkConfigFinal.xml"
$vnet=" Your virtualnetwork"
$subnet = "Your subnet"

#copy VHDs
$diskSource = "source of your your vhd, eg. https://xxx.blob.core.windows.net/vhds/xxx.vhd "
$diskname = "Name of the new vhd in the destination, eg. xxx-new.vhd"
$AzureDiskname = "Name of the new vhs without vhd., eg. xxx-new"
$srcStorageContainer = "Source storagecontainer, eg. vhds"
$destStorageContainer = "Destination storagecontainer, eg. vhds"

#create VM
$servername="name of the new virtuel machine"
$cloudservice ="name of the cloudservice"
Write-Host " "
Write-Host "Step 1. Create Azure affinity group" -ForegroundColor Magenta
Write-Host " "
#create affinitygroup
New-AzureAffinityGroup -Name $affinitygroup -Location $location
# Wait while affinity group is created
Write-Host " "
Write-Host "Step 2. Create Azure storage service" -ForegroundColor Magenta
Write-Host " "
#create storage
New-AzureStorageAccount -StorageAccountName $storage -AffinityGroup $affinitygroup

#get the current subscription
$subscriptionObj = get-AzureSubscription -Default
$subscriptionId = $subscriptionObj.SubscriptionId
set-azuresubscription -SubscriptionId $subscriptionId -CurrentStorageAccountName $storage
select-azuresubscription -SubscriptionId $subscriptionId

Write-Host " "
Write-Host "Step 3. Create Virtual Network" -ForegroundColor Magenta
Write-Host " "
#create network
$dns = New-AzureDns -IPAddress 10.0.0.4 -Name $dnsname
Set-AzureVNetConfig -ConfigurationPath $netconfigpath

Write-Host " "
Write-Host "Step 4. Copy VHD Blob" -ForegroundColor Magenta
Write-Host " "
#copy VHDs - create Storage Container
$destStorageAccountKeyObj = get-azurestoragekey $storage
$destStorageAccountKey = $destStorageAccountKeyObj.Primary
$dest = New-AzureStorageContext $storage $destStorageAccountKey
New-AzureStorageContainer $destStorageContainer -Permission Container -context $dest

#copy VHDs
Start-AzureStorageBlobCopy -AbsoluteUri $diskSource -DestBlob $diskname -DestContext $dest -DestContainer $destStorageContainer

$stateCopy = Get-AzureStorageBlob -Context $dest -Container $destStorageContainer | where {$_.Name -like $diskname}
$stateCopy | Get-AzureStorageBlobCopyState

do
{
Write-Verbose "Checking storage blob copy status every $delaySeconds seconds."
Write-Verbose "This will repeat until all copy operations are complete."

$continue = $false
Get-AzureStorageBlobCopyState -Blob $diskname -Container $destStorageContainer | Format-Table -AutoSize -Property Status,@{label="Percent";expression={"{0:P2}" -f $($_.BytesCopied/$_.TotalBytes)}}, @{label="VHD";expression={$($_.Source).tostring().substring(44,13)}}
$blobStatusPending = Get-AzureStorageBlobCopyState -Blob $diskname -Container $destStorageContainer

# Continue checking status as long as at least one operations is still pending.
if (!$continue)
{
$continue = $blobStatusPending.Status -eq "Pending"
}

if ($continue)
{
Start-Sleep $delaySeconds
}
} while ($continue)

Write-Host " "
Write-Host "Step 5. Define Virtual Disks" -ForegroundColor Magenta
Write-Host " "

$blobsToDisks = Get-AzureStorageBlob -Context $dest -Container $destStorageContainer | where {$_.Name -like $diskname}
Add-AzureDisk -DiskName $AzureDiskName -MediaLocation $blobsToDisks.ICloudBlob.Uri -OS Windows

Write-Host " "
Write-Host "Step 6 Creating VM" -ForegroundColor Magenta
Write-Host " "

$vmName = $servername
$newVM = New-AzureVMConfig -Name $vmName -DiskName $vmName -InstanceSize 'Small' `
| Add-AzureProvisioningConfig -Windows `
| Set-AzureSubnet -SubnetNames $subnet

New-AzureVM -ServiceName $cloudservice -AffinityGroup $affinitygroup -VMs $newVM -VNetName $vnet -DnsSettings $dns -WaitForBoot
Write-Host "Finish!"

Datei NetworkConfigFinal.xml

<NetworkConfiguration xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/ServiceHosting/2011/07/NetworkConfiguration">
<VirtualNetworkConfiguration>
<Dns>
<DnsServers>
<DnsServer name="Your DNS name" IPAddress="10.0.0.4" />
</DnsServers>
</Dns>
<VirtualNetworkSites>
<VirtualNetworkSite name="Your virtualnetwork" AffinityGroup="Your affinitygroup">
<AddressSpace>
<AddressPrefix>10.0.0.0/8</AddressPrefix>
</AddressSpace>
<Subnets>
<Subnet name="Your subnet">
<AddressPrefix>10.0.0.0/11</AddressPrefix>
</Subnet>
</Subnets>
<DnsServersRef>
<DnsServerRef name="Your DNS name" />
</DnsServersRef>
</VirtualNetworkSite>
</VirtualNetworkSites>
</VirtualNetworkConfiguration>
</NetworkConfiguration>

Vielen Dank für Ihr Interesse an meinem Blogeintrag.
Gerne beantworte ich Ihnen persönlich weitergehende Fragen zu diesem Thema.
Kontaktieren Sie mich einfach über mein Kontaktformular.

Sie wollen mehr über mich erfahren? Dann gehen Sie auf meine Site
http://www.nicothiemer.de

Nico Thiemer

Arbeiten mit mehreren Mandaten in der Azure PowerShell

Wenn man wie ich viele Beratungen und/oder Schulungen für Microsoft Azure gibt, hat man nach einer Weile auch einige Testversionen von Microsoft Azure erstellt. In einer deutschen Azure Installation haben diese immer den Namen „Kostenlose Testversion“. Wenn ich jetzt mit meiner Testinstallation in der Azure PowerShell arbeiten möchte, muss ich zuerst eine Konfigurationsdatei importieren. Diese enthält neben dem Name der Installation auch deren eindeutige ID.

Ich gehe davon aus, dass ich im Standardbrowser schon an der Azure Testinstallation angemeldet bin und die Azure PowerShell installiert und offen ist. Mit dem Befehl
Get-AzurePublishSettingsFile
kann ich mir die Installationsdatei besorgen und lokal ablegen. Ist dies die erste Installation, kann ich mit dieser direkt in der PowerShell weiterarbeiten.
Beispiel:
Import-AzurePublishSettingsFile –PublishSettingsFile „C:\Azure\Kostenlose Testversion-8-24-2015-credentials.publishsettings“


Habe ich aber mehrere Installationen, wird es etwas komplizierter. Die Azure PowerShell arbeitet nämlich immer mit der ersten Installation. Wie viele es gibt und deren Daten kann ich mir mit
Get-AzureSubscription
anzeigen lassen.

Im Beispiel oben gibt es insgesamt drei Installationen, alle mit dem gleichen Namen „Kostenlose Testversion“. Wenn ich versuche, zu meiner neuen, aktuellen Installation zu wechseln, wird der folgende Befehl natürlich nicht funktionieren.

Select-AzureSubscription -Default -SubscriptionName „Kostenlose Testversion“

Mit dem Schalter –Default sorge ich dafür, dass die Änderung beständig ist. Das heißt, wenn ich die PowerShell schließe und wieder öffne, soll die Einstellung wieder gelten. Sonst gilt sie nur für die aktuelle Session. Der Name unterscheidet Übrigens zwischen Groß/Kleinschreibung.

In der Beschreibung zu dem Befehl unter https://msdn.microsoft.com/en-us/library/dn495189.aspx steht, der Parameter SubscriptionName ist erforderlich. Also versuche ich es mit

Select-AzureSubscription -Default -SubscriptionName „Kostenlose Testversion“ –SubscriptionId faf0d01c-82ad-482d-bd01-f983f80a3642

Da allerdings erhalte ich den Fehler, dass der Befehl mit diesen Argumenten nicht aufgerufen werden kann. OK, dann versuche ich eben, entgegen der Beschreibung einfach mal den Namen wegzulassen.

Select-AzureSubscription -Default -SubscriptionId faf0d01c-82ad-482d-bd01-f983f80a3642

Und siehe da, es geht!

Irgendwie nicht ganz so logisch, aber gut 🙂

Vielen Dank für Ihr Interesse an meinem Blogeintrag.
Gerne beantworte ich Ihnen persönlich weitergehende Fragen zu diesem Thema.
Kontaktieren Sie mich einfach über mein Kontaktformular.

Sie wollen mehr über mich erfahren? Dann gehen Sie auf meine Site
http://www.nicothiemer.de

Nico Thiemer