Insufficient data from Andrew Fryer

The place where I page to when my brain is full up of stuff about the Microsoft platform

Lab Ops – Working with Azure VMs

A couple of months ago I had a very interesting chat with Andy a Data Centre admin at CP Ltd about using PowerShell to manage Azure as Andy is partially sighted, and his team mate Lewis is blind (For more on this please read Andy’s post on the TechNet UK blog) .  I wanted to go into some of the PowerShell in a little more detail so that you can be a good as administrator on Azure as these guys are.  For this post I am going to assume you know how to work with Azure and are familiar with concepts like storage, cloud services and networking, though you will get an idea if you follow this post!

Firstly to get working with PowerShell on Azure we need to get hold of PowerShell for Azure and remember to check back regularly as they change as Azure changes.

Before we can run any PowerShell against our subscriptions we need to setup some sort of trust otherwise anyone can create services against our subscription.  The simplest way to do this is with

Get-AzurePublishSettingsFile

this will launch the Azure management portal and ask us to sign in. This command will then save a file to our local machine which we can then consume the file like this..

Import-AzurePublishSettingsFile -PublishSettingsFile "C:\AzureManagement\some filename.publishsettings"

However the problem with this approach is that you have access to the whole subscription which is fine for demos and labs. In production you’ll have some sort of Active Directory in place and you'll connect to that with:

$userName = "your account  name" $securePassword = ConvertTo-SecureString -String "your account password" -AsPlainText –Force

$Azurecred = New-Object System.Management.Automation.PSCredential($userName, $securePassword)

Add-AzureAccount -Credential $Azurecred

Now we can run any of the PowerShell for Azure commands against our subscription but before we can do too much with Azure VMs we will need a storage account to store them..

$StorageAccountName = “lowercase with no spaces storage account name”

$AzureLocation = “West Europe”

New-AzureStorageAccount –StorageAccountName $StorageAccountName –Location $AzureLocation 

where –Location specifies the data centre you want the storage account to reside in e.g. West Europe and get-AzureLocation will give you all the data centres you can choose. Now we have a storage account we need to declare that as the default location for our VMs ..

$SubscriptionName = (Get-AzureSubscription).SubscriptionName
Set-AzureSubscription -SubscriptionName $SubscriptionName -CurrentStorageAccountName $AzureStorageAccountName

If you are familiar with VMs in Azure you’ll know that by default each VM get’s its own wrapper or cloud service but in this demo I want to put three of these VMs into the same cloud service which we can create with..

$AzureServiceName = “This needs to be unique on Azure.cloudapp.net”

New-AzureService -ServiceName $AzureServiceName –Location $AzureLocation -Description "Lab Ops cloud service"

Before we can create any VMs we need to also have a network in place and it turns out the PowerShell port for this in Azure is pretty weak all we can do is setup a network using an xml file in the form of

<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="AzureDNS" IPAddress="10.0.0.4" />
      </DnsServers>
    </Dns>
    <VirtualNetworkSites>
      <VirtualNetworkSite name="My-VNet" Location="West Europe">
        <AddressSpace>
          <AddressPrefix>192.168.0.0/16</AddressPrefix>
        </AddressSpace>
        <Subnets>
          <Subnet name="My-Subnet">
            <AddressPrefix>192.168.10.0/24</AddressPrefix>
          </Subnet>
        </Subnets>
      </VirtualNetworkSite>
    </VirtualNetworkSites>
  </VirtualNetworkConfiguration>
</NetworkConfiguration>

you can hack this around and then save it as something like VNnet.xml and then apply it to your subscription with.

$AzureVNet = Set-AzureVNetConfig -ConfigurationPath " path to VNet xml file"

for more on how to hack this file with PowerShell rather than editing it then have a look at Rik Hepworth’s (azure MVP)  blog  –

Now to create those VMs we have more choices –  we could use a template VHD of our own but for now we will  just use the gallery images just as we can in the Azure Management portal.  To do this we need to interrogate the gallery to find the right image with something like this..

$AzureImage = Get-AzureVMimage | where imagefamily -eq "Windows Server 2012 R2 datacenter" | sort-object PublishedDate -Descending  | select-object -first 1

which will get the most recent gallery image for Windows Server 2012R2 datacenter edition. I can then consume this in a script to create a VM

$AdminUser = "deepfat"
$adminPassword = "Passw0rd!"

New-AzureVMConfig -Name $VMName -InstanceSize Medium -ImageName $AzureImage.ImageName | `
        Add-AzureProvisioningConfig –Windows -AdminUsername $AdminUser –Password $AdminPassword | `
        Set-AzureSubnet 'Deepfat-Prod' |`
        New-AzureVM –ServiceName $AzureServiceName –Location $AzureLocation -VNetName $AzureVNet

Note that if you want to embed these snippets  in a script you’ll need to get clever and introduce some wait loops to allow the VMs to spin up.

By default when you create a VM a couple of endpoints will be created one for RDP and one for PowerShell.  In reality you wouldn’t necessarily want to do this as you may have site to site VPN in which case this is redundant or you might just do this on one VM to manage the rest or use Azure Automation.  We need to query for these ports as in a cloud service each VM will have the same DNS entry but with different random ports:

$VM = Get-AzureVM -ServiceName $AzureServiceName -Name $VMName

$VMPort = (Get-AzureEndpoint -Name PowerShell -VM $VM).port

In Andy’s post he published a self signed certificate to his cloud service which is needed to enable a secure remote PowerShell session to the VM.  However if we are just trying this in a lab then we can use the certificate that Azure automatically creates when a cloud service is created as this is also trusted by the VMs in that cloud service by default.  We can then pull this down and trust it on our local machine with

(Get-AzureCertificate -ServiceName $AzureServiceName -ThumbprintAlgorithm SHA1).Data | Out-File "${env:PUBLIC}\CloudService.cer"
Import-Certificate -FilePath  "${env:PUBLIC}\CloudService.cer" -CertStoreLocation Cert:\LocalMachine\AuthRoot

Now we have all the setting and permissions we need to setup a remote PowerShell session to our VM..

$VMCred = new-object -typename System.Management.Automation.PSCredential -argumentlist $AdminUser, (ConvertTo-SecureString $adminPassword -AsPlainText -Force  )

$VMSession = New-PSSession -ComputerName ($AzureServiceName + ".cloudapp.net") -Port $WFE1Port -Credential $VMCred -UseSSL -Name WFE1Session

with this session we can now add in roles and features, turn on firewall rules and so on like this

Invoke-Command -Session $WFE1Session -ScriptBlock {
    Get-WindowsFeature Web-server | Add-WindowsFeature –IncludeAllSubFeature}

If we want to work on a SQL server VM (there’s a bunch of gallery items on Azure with different editions of SQL Server on) then it might be useful to enable SQL Server mixed mode authentication in which case we need to pass parameters into the session and the simplest way to do this is by using the param() setting inside the script block with and –ArgumentList switch at the end (remembering to keep the parameters in the same order..

Invoke-Command -Session $VMSession -ScriptBlock { param($VMCred, $VMName)
    #set SQL Server to mixed mode and restart the service in the process
    Get-SqlInstance  -machinename $VMName -credential $VMCred -AutomaticallyAcceptUntrustedCertificates |  Set-SqlAuthenticationMode  -Mode Mixed -Credential $VMCred -ForceServiceRestart -SqlCredential $VMCred
      }
ArgumentList $VMCred, $VMName

as this allows us to reuse the parameters we are already working with in the remote session and enhances readability.

So that’s a quick introduction to some of the stuff that the more enlightened IT Professionals like Andy are using to make their lives easier and actually a lot of the stuff in this post works in your own data centre (like the stuff at the end to setup SQL Server) so using Azure really is just an extension of what you are used to.

Be warned –  stuff keeps changing on Azure.
For example a lot of older examples use Affinity Groups in azure to co-locate VMs but this is on the way out so I deliberately didn’t reference that here.  My advice is to be wary of older posts and follow the Azure blog particularly if what you are trying to do is still in preview