Tag Archives: microsoft

[How-To] Deploy HUB Licensed VMs in Azure

What is HUB and why should I use it?

For customers that are looking to go to Azure and help cut down on some of the running costs by utilizing existing licensing they own from on-premises, Microsoft has released a program called Hybrid Use Benefit (HUB).  The Hybrid Use Benefit program essentially allows you to run  VMs in Azure at a reduced rate (cost of a Linux VM for example), under the assumption that you have volume license keys covering the core totals of VMs running in Azure.

Official information on this program can be found here: https://azure.microsoft.com/en-us/pricing/hybrid-use-benefit/

A Microsoft employee has created some instructions on how Microsoft customers can take use of this program.  Unfortunately, as outlined in the document, there is no way as of 6/27/2016 to take an existing VM in Azure and convert it to the HUB program.  A VM must originate from on-premises in order to take advantage of this program, there are no workarounds such as building the VM in Azure, downloading it to on-prem, and reuploading it back to Azure.  In this case, this article will cover the necessary steps (step by step) on getting a “HUB” VM deployed in Azure.

Goal: This article will focus on 3 items:

  1. How to properly configure a VHD for the HUB program
  2. How to upload the VHD into Azure
  3. How to deploy VMs from your VHD

1. How to properly configure a VHD for the HUB program

There are two ways you can bring a HUB image into Azure.  You can convert the ISO from Microsoft to a VHD directly, or you can install Hyper-V, update/customize the VM, and generalize it.

In this tutorial, we will go over converting the Microsoft provided ISO to VHD, under the assumption you do not have Hyper-V installed.  In the scenario where you do not have Hyper-V, but you want to customize the image before uploading it into Azure, I would recommend installing the Hyper-V role on your Windows 7/10 machine and creating the VHD from that.  The only caveat you will run into is you must run SysPrep before uploading the VHD into Azure, as outlined here.

Hyper-V Way

For the Windows 7/10 machines, you can install the Hyper-V role by navigating to Programs and Features, select Turn Windows features on or off
Control Panel - Programs and Features - Turn windows features on or off

Check Hyper-V from the list.
Control Panel - Programs and Features - Turn windows features on or off - Hyper-V

Additionally, installation via PowerShell or DISM is covered in this Microsoft blog post: https://msdn.microsoft.com/en-us/virtualization/hyperv_on_windows/quick_start/walkthrough_install

Again, ensure after making changes to your VHD, you generalize the machine and shut it down as outlined here: https://azure.microsoft.com/en-us/documentation/articles/virtual-machines-windows-upload-image/

PowerShell Way

A few Microsoft employees/consultants have released a PowerShell script that will take a Microsoft provided ISO and convert/generalize it into VHD format.  We can simply take this converted VHD and upload it into Azure as-is.  Please note, the only downside to creating the VM this way is that the machine may not be completely patched, so you will have to rely on the machines doing Windows Updates once they make it into the lands of Azure.

Pro Tip: If you are going to create the VHD from an ISO, I would recommend doing this from an Azure VM.  Since the VHD/disk we are creating will originate from the source media of a “local instance”, your VM will deploy fine with HUB licensing.  The advantage of creating the VM in Azure is the upload of your VM will take significantly less time due to the high throughput of egress traffic in Azure.  When going through this tutorial, I ended up maxing out my storage account’s read rate before hitting network connectivity bottlenecks.  Please note, bandwidth fees may apply in Azure for utilizing this method though.

  1. Download the Convert-WindowsImage.ps1 script from Microsoft
    1. https://gallery.technet.microsoft.com/scriptcenter/Convert-WindowsImageps1-0fe23a8f
  2. Download your Windows Server ISO from the Microsoft Volume Licensing center
    1. https://www.microsoft.com/Licensing/servicecenter/default.aspx
  3. Open PowerShell as an Administrator
    Server 2012 - PowerShell - Run as Administrator
  4. Navigate to the directory that contains both the ISO and the Covert-WindowsImage.ps1 script
    Convert-WindowsImage and Windows Server ISO - PowerShell
  5. Execute the following command to pre-load (dot-source) the PowerShell function
    Convert-WindowsImage and Windows Server ISO - PowerShell - Load Function

    . .\Convert-WindowsImage.ps1
  6. Execute the following command
    Convert-WindowsImage and Windows Server ISO - PowerShell - Execute Function

    Convert-WindowsImage -SourcePath "en_windows_server_2012_r2_with_update_x64_dvd_6052708.ISO" -VHDFormat VHD -Edition "ServerDataCenterCore" -VHDPartitionStyle MBR -BCDinVHD NativeBoot -ExpandOnNativeBoot:$false -RemoteDesktopEnable -Verbose
  7. You should receive a “Done” message once the VHD has been created
    Convert-WindowsImage and Windows Server ISO - PowerShell - Execute Function -Completed

2. How to upload the VHD into Azure

First, you will need the latest Azure PowerShell Modules.  These can be downloaded for free from the Azure website.  If you are new to Azure, this will be a link to the Web Platform installer, in which the link below should automatically select the Azure PowerShell modules to be downloaded.  You do not need the Command Line installer if prompted, only the Azure PowerShell Modules.


Web Platform Installer 5 - Microsoft Azure PowerShell

Once installed, complete the instructions below.

  1. Open up PowerShell
    Server 2012 - PowerShell - Run as Administrator
  2. Login to your Azure account

  3. Execute the following command below, substituting in the correct values applicable to your environment:
    -RessourceGroupName – Specifies the name of the resource group of the virtual machine.
    -Destination – Specifies the URI of a blob in Blob Storage. The parameter supports SAS URI, although patching scenarios destination cannot be an SAS URI.  My URL shows Premium storage, but Premium storage is not required for HUB.
    -LocalFilePath – Specifies the path of the local .vhd file.
    Login-AzureRmAccount - Add-AzureRmVhd - Completed

    Add-AzureRmVhd -ResourceGroupName Test -Destination "https://armpremiumstoragetest.blob.core.windows.net/vhds/WindowsServer2012R2-HUB-Image.vhd" -LocalFilePath "E:\Blog\9600.17415.amd64fre.winblue_r4.141028-1500_Server_ServerDatacenterCore_en-US.vhd"

3. How to deploy VMs from your VHD

Copy the template below:

  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "",
  "parameters": {
    "vmName": {
      "type": "string",
      "metadata": {
        "description": "Name of the VM you wish to deploy"
	"VMStorageAccount": {
      "type": "string",
      "metadata": {
        "description": "This is the name of the your storage account to deploy the VM to"
	"virtualNetworkName": {
      "type": "string",
      "metadata": {
        "description": "Name of the virtual network the VM should be deployed to"
	"subnetName": {
      "type": "string",
      "metadata": {
        "description": "Name of the subnet the VM should be deployed to"
	"publicIPAddressName": {
      "type": "string",
      "metadata": {
        "description": "Name of the public IP address for your VM."
    "dnsLabelPrefix": {
      "type": "string",
      "metadata": {
        "description": "DNS Label for the Public IP. Must be lowercase. It should match with the following regular expression: ^[a-z][a-z0-9-]{1,61}[a-z0-9]$ or it will raise an error."
    "adminUserName": {
      "type": "string",
      "metadata": {
        "description": "UserName for the Virtual Machine"
    "adminPassword": {
      "type": "securestring",
      "metadata": {
        "description": "Password for the Virtual Machine"
	"publicIPAddressType": {
      "type": "string",
      "allowedValues": [
	  "defaultValue": "Dynamic",
      "metadata": {
        "description": "IP Address type for the public IP of the VM"
    "vmSize": {
      "type": "string",
      "metadata": {
        "description": "This is the size of your VM"
	  "defaultValue": "Standard_DS1_v2"
  "variables": {
    "location": "[resourceGroup().location]",
    "nicName": "[concat(parameters('vmName'),'nic')]",
	"osDiskVhdUri": "https://myosdiskvhduri.blob.core.windows.net/vhds/myimage.vhd",
	"osType": "Windows",
    "vnetID": "[resourceId('Microsoft.Network/virtualNetworks',parameters('virtualNetworkName'))]",
    "subnet1Ref": "[concat(variables('vnetID'),'/subnets/',parameters('subnetName'))]",
    "osDiskVhdName": "[concat('https://',parameters('VMStorageAccount'),'.blob.core.windows.net/vhds/',parameters('vmName'),'-osDisk.vhd')]",
    "apiVersion": "2015-06-15"
  "resources": [
      "apiVersion": "[variables('apiVersion')]",
      "type": "Microsoft.Network/publicIPAddresses",
      "name": "[parameters('publicIPAddressName')]",
      "location": "[variables('location')]",
      "properties": {
        "publicIPAllocationMethod": "[parameters('publicIPAddressType')]",
        "dnsSettings": {
          "domainNameLabel": "[parameters('dnsLabelPrefix')]"
      "apiVersion": "[variables('apiVersion')]",
      "type": "Microsoft.Network/networkInterfaces",
      "name": "[variables('nicName')]",
      "location": "[variables('location')]",
      "dependsOn": [
        "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]"
      "properties": {
        "ipConfigurations": [
            "name": "ipconfig1",
            "properties": {
              "privateIPAllocationMethod": "Dynamic",
              "publicIPAddress": {
                "id": "[resourceId('Microsoft.Network/publicIPAddresses',parameters('publicIPAddressName'))]"
              "subnet": {
                "id": "[variables('subnet1Ref')]"
      "apiVersion": "[variables('apiVersion')]",
      "type": "Microsoft.Compute/virtualMachines",
      "name": "[parameters('vmName')]",
      "location": "[variables('location')]",
      "dependsOn": [
        "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
      "properties": {
        "licenseType": "Windows_Server",
        "hardwareProfile": {
          "vmSize": "[parameters('vmSize')]"
        "osProfile": {
          "computerName": "[parameters('vmName')]",
          "adminUsername": "[parameters('adminUsername')]",
          "adminPassword": "[parameters('adminPassword')]"
        "storageProfile": {
          "osDisk": {
            "name": "[concat(parameters('vmName'),'-osDisk')]",
            "osType": "[variables('osType')]",
            "caching": "ReadWrite",
            "createOption": "FromImage",
            "image": {
              "uri": "[variables('osDiskVhdUri')]"
            "vhd": {
              "uri": "[variables('osDiskVhdName')]"
        "networkProfile": {
          "networkInterfaces": [
              "id": "[resourceId('Microsoft.Network/networkInterfaces',variables('nicName'))]"
        "diagnosticsProfile": {
          "bootDiagnostics": {
             "enabled": "true",
             "storageUri": "[concat('https://',parameters('VMStorageAccount'),'.blob.core.windows.net')]"
  1. Login to the Azure Portal
    1. https://portal.azure.com
  2. Select Browse, click Templates, click Add
    Azure - Browse - Templates - Add
  3. Type in a Name and Description, click OK
    Azure - Browse - Templates - Add - General
  4. Paste in the template above, ensure you change the osDiskVhdUri, and click OK
    Azure - Browse - Templates - Add - General - change uri
  5. Once you have deploy the script, click Add, select your template, and click DeployDeploy from Template

At this point, you should be able to deploy from the template and create your VM from the HUB licensed VHD! 🙂

Notes: The above script takes into account that you are deploying against standard storage.  You may need to edit the above script if you want to deploy to premium storage as diagnostics data does not support being deployed to a Premium Storage account as of 6/27/2016.

Notes: The above script assumes your Virtual Network and Subnet have been previously created.  It will not create a virtual network and subnet if they do not exist.

Tutorial – How to setup a KMS server for a Windows Domain

Copied from Microsoft, here is what we can achieve by configuring a KMS server on our local network for a windows domain: http://technet.microsoft.com/en-us/library/ff793434.aspx

KMS activates computers on a local network, eliminating the need for individual computers to connect to Microsoft. To do this, KMS uses a client–server topology. KMS client computers can locate KMS host computers by using Domain Name System (DNS) or a static configuration. KMS clients contact the KMS host by using remote procedure call (RPC). KMS can be hosted on computers that are running the Windows Vista, Windows 7, Windows Server 2003, Windows Server 2008, or Windows Server 2008 R2 operating systems.

  1. Go to the volume licensing center and grab a copy of the KMS key for your server OS
    1. Navigate to https://www.microsoft.com/Licensing/servicecenter/home.aspx
    2. Login
    3. Select Downloads and Keys
      Volume Licensing Service Center - Downloads and Keys
    4. Select Windows Server
      Volume Licensing Service Center - Windows Server
    5. Finder your server version and click Key
      Volume Licensing Service Center - Windows Server - Key
    6. Copy the KMS type key
  2. Login to the server you want to setup as the KMS server.
  3. Open up a command prompt as an administrator.
  4. Ensure you are in the system32 folder of Windows
    1. cd c:\Windows\System32
      windows - System 32
  5. Execute the following command to setup your license key
    1. cscript slmgr.vbs /ipk WINDOWS-KMS-LICENSE-KEY-HERE
      cscript slmgr ipk
  6. Execute the following command to activate the host
    1. cscript slmgr.vbs /ato
      Activating Windows
  7. Execute the following command to verify the host has the Key Management Service enabled
    1. cscript slmgr.vbs /dlv
      cscript slmgr dlv
  8. Next, we need to open the firewall for the server to accept activation requests
    1. Open up Windows Firewall with Advanced Security
      Windows 8 - Windows Firewall with Advanced Security
    2. Right click on Inbound Rules and select New Rule…
      Windows Firewall with Advanced Security - New Rule
    3. Select Port and click Next >
      New Inboud Rule Wizard - Port
    4. Check TCP, check Specific Local Ports and enter port 1688, click Next >
      New Inboud Rule Wizard - Specific local ports
    5. Check Allow the connection and click Next >
      New Inboud Rule Wizard - Allow the connection
    6. Check Domain and click Next >
      New Inboud Rule Wizard - Domain
    7. Enter a name for the rule and click Finish
      New Inboud Rule Wizard - Rule Name

Congrats!  Your KMS server should now be ready to accept activation requests!

Notes: Here is a full listing of the commands/switches you can execute using the Software Licensing Management Tool.

C:\Windows\System32>cscript slmgr.vbs
Microsoft (R) Windows Script Host Version 5.8
Copyright (C) Microsoft Corporation. All rights reserved.

Invalid combination of command parameters.

Windows Software Licensing Management Tool
Usage: slmgr.vbs [MachineName [User Password]] [<Option>]
MachineName: Name of remote machine (default is local machine)
User: Account with required privilege on remote machine
Password: password for the previous account

Global Options:
/ipk <Product Key>
Install product key (replaces existing key)
/ato [Activation ID]
Activate Windows
/dli [Activation ID | All]
Display license information (default: current license)
/dlv [Activation ID | All]
Display detailed license information (default: current license)
/xpr [Activation ID]
Expiration date for current license state

Advanced Options:
Clear product key from the registry (prevents disclosure attacks)
/ilc <License file>
Install license
Re-install system license files
Reset the licensing status of the machine
/upk [Activation ID]
Uninstall product key

/dti [Activation ID]
Display Installation ID for offline activation
/atp <Confirmation ID> [Activation ID]
Activate product with user-provided Confirmation ID

Volume Licensing: Key Management Service (KMS) Client Options:
/skms <Name[:Port] | : port> [Activation ID]
Set the name and/or the port for the KMS computer this machine will use. IPv6 address must be specified in the format [hostname]:port
/ckms [Activation ID]
Clear name of KMS computer used (sets the port to the default)
/skms-domain <FQDN> [Activation ID]
Set the specific DNS domain in which all KMS SRV records can be found. This setting has no effect if the specific single KMS host is set via /skms option.
/ckms-domain [Activation ID]
Clear the specific DNS domain in which all KMS SRV records can be found. The specific KMS host will be used if set via /skms. Otherwise default KMS auto-discovery will be used.
Enable KMS host caching
Disable KMS host caching

Volume Licensing: Token-based Activation Options:
List installed Token-based Activation Issuance Licenses
/ril <ILID> <ILvID>
Remove installed Token-based Activation Issuance License
List Token-based Activation Certificates
/fta <Certificate Thumbprint> [<PIN>]
Force Token-based Activation

Volume Licensing: Key Management Service (KMS) Options:
/sprt <Port>
Set TCP port KMS will use to communicate with clients
/sai <Activation Interval>
Set interval (minutes) for unactivated clients to attempt KMS connection. The activation interval must be between 15 minutes (min) and 30 days (max) although the default (2 hours) is recommended.
/sri <Renewal Interval>
Set renewal interval (minutes) for activated clients to attempt KMS connection. The renewal interval must be between 15 minutes (min) and 30 days (max) although the default (7 days) is recommended.
Enable DNS publishing by KMS (default)
Disable DNS publishing by KMS
Set KMS priority to normal (default)
Set KMS priority to low
/act-type [Activation-Type] [Activation ID]
Set activation type to 1 (for AD) or 2 (for KMS) or 3 (for Token) or 0 (for all).

Volume Licensing: Active Directory (AD) Activation Options:
/ad-activation-online <Product Key> [Activation Object name]
Activate AD (Active Directory) forest with user-provided product key
/ad-activation-get-iid <Product Key>
Display Installation ID for AD (Active Directory) forest
/ad-activation-apply-cid <Product Key> <Confirmation ID> [Activation Object name]
Activate AD (Active Directory) forest with user-provided product key and Confirmation ID
Display Activation Objects in AD (Active Directory)
/del-ao <Activation Object DN | Activation Object RDN>
Delete Activation Objects in AD (Active Directory) for user-provided Activation Object

[Office 365] Delete a user account sitting in Recycle bin

Normally, user accounts that are deleted within Office 365 sit in “The recycle bin” where they can be recovered if needed. You can’t, however, delete users from that gray area within the web GUI. If you wanted to, say, delete and remove the license from a user and create a non-licensed shared mailbox, you’re boned without emptying it from the recycle bin first.

Before you start,you’ll need Microsoft Online Services Module for Powershell

  • http://onlinehelp.microsoft.com/Office365-enterprises/ff652560.aspx

To remove a specific user account from the Recycle Bin

  1. Connect normally, except add in connecting to the MSO server
    1. $LiveCred = Get-Credential
    2. $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell/ -Credential $LiveCred -Authentication Basic -AllowRedirection
    3. Import-PSSession $Session
    4. Connect-MSOLService -Credential $LiveCred
  2. The ReturnDeletedUsers switch returns accounts found in the recycle bin. To return all:
    1. Get-MSOLUser -ReturnDeletedUsers
  3. If you find the account you want to remove, it’s a simple cmdlet to do so: where “Email Address” is the upn of the actual account
    1. Remove-MSOLUser -UserPrincipalName “Email Address” -RemoveFromRecycleBin
  4. If you’re removing an account in order to recreate it, you’ll have to wait 5-10 minutes before O365 will allow you to recreate over the deleted account.

If you don’t have a problem nuking them all, you can empty all via piping the get to the remove

#Haven’t tested this one; have a nagging you may need to fill an array and foreach through all the elements actually.

Get-MsolUser -ReturnDeletedUsers | Remove-MsolUser -RemoveFromRecycleBin -Force