Part 1: Deploy Azure Stack Single Node POC

Welcome back if you’ve been here before, and ahoy there if you haven’t

Over the last couple of years I’ve been heavily focused on Server 2016 and many of its new features, with that now being handed over to operations my focus is now shifting to Azure Stack. As my boss has been involved with Azure Stack since the very early days, I’ve had the opportunity to dip in and out over the last 6 months or so, I’m therefore not going in completely blind…which is nice

As with any new technology, the first thing I did was go and get a look at the available documentation…and there is a LOT of it available already, most of it can be found HERE

We’re now at Technical Preview 3 for Azure Stack and in my experience the deployment is pretty straightforward now and mainly without issue. As we’re not quite at GA yet, AS can only be deployed on a single server for Proof of Concept purposes and that’s what this guide is going to step through. Here’s a high level of what steps are included:

It’s worth noting at this point that everything I’ll be going through in this guide will be from the perspective of an Azure Stack admin. I’ll be uploading a guide at a later date to step through the creation of Plans, Offers and Quotas and how we use these to offer out some of this guides services to our tenants.

That’ll do it for the overview, let’s dive in


As the Azure Stack POC is installed on a single piece of tin, the resource requirements are pretty high, as you work through this guide though, the why becomes obvious fairly quickly. The table below shows both the minimum and recommended requirements:

Disk drives: Operating System1 OS disk with minimum of 200 GB available for system partition (SSD or HDD)1 OS disk with minimum of 200 GB available for system partition (SSD or HDD)
Disk drives: General Azure Stack POC Data*4 disks. Each disk provides a minimum of 140 GB of capacity (SSD or HDD). All available disks will be used.4 disks. Each disk provides a minimum of 250 GB of capacity (SSD or HDD). All available disks will be used.
Compute: CPUDual-Socket: 12 Physical Cores (total)Dual-Socket: 16 Physical Cores (total)
Compute: Memory96 GB RAM128 GB RAM (This is the minimum to support PaaS resource providers.)
Compute: BIOSHyper-V Enabled (with SLAT support)Hyper-V Enabled (with SLAT support)
Network: NICWindows Server 2012 R2 Certification required for NIC; no specialized features requiredWindows Server 2012 R2 Certification required for NIC; no specialized features required
HW logo certificationCertified for Windows Server 2012 R2Certified for Windows Server 2012 R2

You can read a little more on SLAT HERE

You can find some instructions for checking if your CPU is SLAT capable HERE – I’d suggest it’s probably easier just looking up the manufacturer specs of your CPU instead though.

If your server meets the recommended resources detailed in the above table, you’ll be able to deploy all the items listed in this guide, however, if you also want to deploy some of the services from the market place, you may need a little more. You can check the viability of your physical host by downloading and running THIS PowerShell script provided by Microsoft.

Any disks you use must be of the same type (SAS or SATA) and if you’re using SAS they’ll need to be connected via a single path as MPIO (Multi-Path IO) is not supported.

HBA Configuration Options:

  • (Preferred) Simple HBA
  • RAID HBA – Adapter must be configured in “pass through” mode
  • RAID HBA – Disks should be configured as Single-Disk, RAID-0

Supported bus and media type combinations

RAID SSD (If the media type is unspecified/unknown*)

*NOTE:  RAID controllers that cannot be set as “Pass-Through” will not recognise the disk media type. For that reason, both SSDs and HDDs will be presented to the OS as “Unspecified”. When you go on to deploy Azure Stack in this scenario, both SSDs and HDDs will be used as persistent storage and you’ll be without a cache layer.

The operating system you install on the host should be 2012 R2 or later. This OS isn’t the one you’ll be going forward with though as one of the first steps in the deployment is to set the host to boot from a preconfigured VHDX.

Microsoft have provided a PowerShell script you can use to check the viability of the hardware you’re using for your Azure Stack deployment. You can find it HERE.

Account Requirements

You have two options here, and the main reason for choosing between the two will be whether your Azure Stack server has access to the internet or not. You’re options are as follows:

Azure Active Directory (Azure AD)

We’ll be making use of this method in the guide.

You’ll be asked to provide the details to an account within your Azure Active Directory during deployment but as above, you’re Azure Stack server will need internet access for this. If you already have an Azure AD account that is the directory administrator for at least one Azure AD, you can use this, alternatively, you can create a free Azure account HERE.

NOTE:  I’ve just noticed that if you’re in China, you’ll need to follow THIS link instead.

You’ll also want to create an account to connect to Azure Stack as a tenant, but we’ll cover that later in the guide.

Active Directory Federation Services (ADFS)

The Azure Stack deployment comes with its own ADFS and Active Directory Domain Services (ADDS) instances. So this option would be your choice if your Azure Stack server either doesn’t have internet access or you don’t want to (or don’t have) use Azure AD.


The networking requirements for the Azure Stack POC are fairly straightforward, you’ll need a single port on a switch and it doesn’t require any specialised features. The port can be configured as either an access or trunk port. Either way, I’d suggest setting the VLAN as “Native” on the port as it’ll save you specifying the VLAN ID during the Azure Stack deployment.

When choosing a subnet for your Azure Stack environment, do not use any of the following as these are reserved for internal Azure Stack networks:


NOTE:  Only IPv4 is supported on the Azure Stack POC

You can either provide your Azure host with an IP using DHCP or it can be statically assigned. You will have to provide the static IP details when running the Azure Stack deployment script, but we’ll cover this at the time.

As explained above, most Azure Stack POCs will be deployed with access to the internet (ports 80 and 443 required). This access needs to be direct or via a transparent proxy.

NOTE:  Azure Stack does not support the configuration of a web proxy to access the internet


The physical host you’re using will need to have .NET Framework 4.6 or later installed.

When deploying our Azure Stack POC, we’ll be setting the physical host to boot from VHDX, so we’ll first need to download that file and a few others, here’s a little detail on each:

  • CloudBuilder.vhdx – This virtual disk contains a partially configured OS drive that will form the basis of our Azure Stack POC server.
  • BootMenuNoKVM.ps1 – A PowerShell script to prepare the physical host to boot into the CloudBuilder.vhdx, used if no KVM to the host is avaiable.
  • unattend_NoKVM.xml – The unattend file paired with the above PowerShell script.
  • PrepareBootFromVHD.ps1 – Same as above but assumes you have KVM to the physical host…the method I’ll be using in this guide.
  • Unattend.xml – The unattend file paired with the above PowerShell script and the one we’ll be using in this guide.

Download the Cloud Builder VHDX

Before continuing, the machine you’re working from will need at least 60GB of available disk space.

To download the Azure Stack POC, you’ll have to follow the registration process HERE
Click the link to launch the downloader and run it or save it locally.


Once launched, click “Browse” and select a download location for the files, you’ll need about 16GB of available space. The correct build should be selected by default.

Now click “Download”


Now go make a coffee, this may take a while depending on your available bandwidth…or it you don’t drink coffee, you can click “Details” to see what files are being downloaded


Once downloaded, click “Run” to launch the Self-Extractor, as it’s about to inflate a file from 15.5GB to just under 40GB, it’ll take a while.


Run through the wizard and select an extraction location that has at least 40GB available.

Download Azure Stack Support Files

The rest of the files we’ll need can be downloaded using some PowerShell provided by MS.

Launch an administrative PowerShell ISE console, paste in the following code and run it:

# Variables
$Uri = ""
$LocalPath = "c:\AzureStack_SupportFiles"

# Create folder
New-Item $LocalPath -type directory

# Download files
( 'BootMenuNoKVM.ps1', 'PrepareBootFromVHD.ps1', 'Unattend.xml', 'unattend_NoKVM.xml') | `
foreach { Invoke-WebRequest ($uri + $_) -OutFile ($LocalPath + '\' + $_) }

PHEW, that about covers it for the prerequisites. Let’s move on to the fun part…the deployment.

Deploy Azure Stack POC

The following process assumes that you have KVM (or direct console) access to the physical host as you’ll need it to continue.

Now log onto the host as the local administrator.

Copy the CloudBuilder.vhdx you extracted earlier to the root of the C:\ drive (C:\CloudBuilder.vhdx)

Launch an elevated PowerShell console and change directory to C:\AzureStack_SupportFiles and run the following code:

.\PrepareBootFromVHD.ps1 -CloudBuilderDiskPath C:\CloudBuilder.vhdx -ApplyUnattend -AdminPassword "LocalAdminPassword"

The server will now be set to boot from the CloudBuilder.vhdx file.

You will be asked to confirm the following:

‘Performing the operation “Enable the Local shutdown access rights and restart the computer”’’ – Select “Yes” and the server will reboot.


Jump across to your KVM and launch a console to the physical host.

Once the server is up and running, log on as the local administrator using the password you specified in the PowerShell above.

We’re almost in a place where we can kick off the Deployment of Azure Stack itself. Before that though there are a few things we need to/should do first.

As the Azure Stack POC only supports a single NIC, make sure you’ve only got one connected and disable all others. The following PowerShell will do this for you:

Get-NetAdapter | ? Status -ne "Up" | Disable-NetAdapter -Confirm:$false

If you’re not using DHCP, configure a static IP and DNS server(s) on the remaining enabled NIC.

Change the name of your host to something more meaningful and reboot e.g. AS-POC1

Change Time Zone

When it comes to deploying the App Service later, I’ve found that this’ll fail unless the Time Zone is set to “(UTC) Coordinated Universal Time”. I’ve also found that leaving the NTP server as “” gives me inconsistent results. With this in mind, let’s change them both before continuing.

Right-Click the Start Menu and select “Control Panel”
Select “Date and Time”
Click “Change time zone”, select “(UTC) Coordinated Universal Time” and click “OK”


Select the “Internet Time” tab and click “Change settings”

Change the NTP server to one of your choice and click “Update now” – if this step fails to sync, just click it again.

Now click “OK” twice to exit.


Now that we’ve sorted our time zone and time, we’ll want the VMs that are spun up during the Azure Stack deployment to inherit these settings from the host. We can make sure this happens by enabling the “Time Synchronization” integration service on the VMs.

I’ve elected to do this by creating a PowerShell script that checks all VMs for any where the Time Sync service isn’t enabled and enables it, it’ll also update the time zone for any VMs deployed to UTC. This script runs as a scheduled task that executes it every 2 minutes for 24 hours…it seems overkill but just in case your Azure Stack deployment is REALLY slow.

Open PowerShell ISE and paste the following code into the editor:

Get-VM | Get-VMIntegrationService -Name "Time Synchronization" | ? Enabled -eq $false | Enable-VMIntegrationService

$VMs = (Get-VM).Name
foreach ($VM in $VMs)
        Invoke-Command -ComputerName $VM -ScriptBlock {
                                                      $TimeZone = (Get-TimeZone).Id
                                                      if ($TimeZone -ne "UTC")
                                                        Set-TimeZone -Id "UTC"

Now save the file as “C:\Scripts\EnableTimeSync.ps1”

Now launch “Task Scheduler”, right-click “Task Scheduler Library” and select “Create Task”

  • Give your task a name e.g. “Enable Time Sync on VMs”
  • Select “Run whether user is logged on or not”
  • Select “Run with highest privileges”
  • Select “Configure for: Windows Server 2016”
  • Select the “Triggers” tab and click “New”
  • Keep the default of “One time” highlighted and set the start time to be 10 minutes ahead of your current time
  • Under “Advanced Settings”, place a tick in “Repeat task every” and set it to “2 minutes” with a duration of “1 day”
  • Now click “OK”
  • Select the “Action” tab and click “New”
  • Select “Start a program” from the “Action” drop-down
  • Enter “C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe” in the “Program/script” text box
  • Enter “C:\Scripts\EnableTimeSync.ps1” into the “Add arguments (optional)” text box
  • Now click “OK” twice to save the task
  • Enter the password for the local administrator account when prompted

NOTE:  when you click “OK” to save the task, you’ll be asked to enter the password for the current user, in this case the local administrator account.

Now right-click the task you just created and select “Run”. Monitor the “Last Run Time” and “Next Run Time” columns for the next few minutes to make sure it’s running automatically as expected.


Now that we’ve sorted all that out, let’s get on and actually deploy the thing.

Run Azure Stack Deployment Script

For this example I’m not using DHCP and so will be statically assigning an IP address to my BGPNAT VM as part of the code. I’ll also be using an Azure Active Directory (AAD) account and will be providing that in the code too.

The following table give you a little detail on what you should be inserting for the script variables.

$AdminPassThis should be the same password you used for the physical host administrator account (the one you're currently logged on with)
$AADPassThis should be the password for your Azure Active Directory account
$AADCredThis should be the username for your Azure Active Directory account in the following format:
$NTPServerEnter the NTP server of your choice. Pretty sure this doesn't actually do anything at this point but I set it anyway 🙂

NOTE:  Do not run this code from within PowerShell ISE and it’s been known to cause issues

Open up an elevated PowerShell console and paste in the following code after updating the variables and IP information for your environment:

cd C:\CloudDeployment\Setup
$AdminPass = ConvertTo-SecureString "Passw0rd1" -AsPlainText -Force
$AADPass = ConvertTo-SecureString "PassW0rd1" -AsPlainText -Force
$AADCred = New-Object System.Management.Automation.PSCredential ("", $aadpass)

.\InstallAzureStackPOC.ps1 -AdminPassword $AdminPass -InfraAzureDirectoryTenantAdminCredential $AADCred -NatIPv4Subnet -NatIPv4Address -NatIPv4DefaultGateway -TimeServer $NTPServer

All going well, it should look something like this to start with:


Once the script hits a certain point, it’ll reboot the system. When that happens, log back on as AzureStack\AzureStackAdmin using the password you provided in the script. If you accidentally log in as .\Administrator, you won’t see the progress of the deployment.

The script will take roughly 4-6 hours to complete depending on hardware and there will be no red in sight all going well

Your screen should look something like this when it’s finished…


Having previously deployed both TP1 and TP2, I sat there waiting for the angels to come down and congratulate me for a job well done. Apparently that’s no longer the case though as the deployment process now works without a hitch…ahh well, no angels for me.

Post Deployment Steps

Activate Administrator and Tenant Portals

Only a few things left to do folks and we can consider this one done. Firstly, we’ll want to activate both the administrator and tenant portals.

Launch the Admin Portal and login using the AAD account you used when deploying Azure Stack. Click accept when prompted.

Repeat the same process for the Tenant Portal

Reset Password Expiration to 180 Days

By default, the password expiry is set to 42 days, we’re going to go ahead and change this to 180 days.

Launch an elevated PowerShell console and enter the following code:

Set-ADDefaultDomainPasswordPolicy -MaxPasswordAge 180.00:00:00 -Identity azurestack.local

You can confirm the command had the desired effect by typing:

Get-ADDefaultDomainPasswordPolicy | FT MaxPasswordAge

The output should look as follows:


Create and Upload Server 2016 Default Image to Gallery

Although IaaS isn’t generally the best use case for Azure Stack, chance are, the first thing you’re going to deploy for testing purposes is a VM As such, we’re now going to prepare and upload a default Server 2016 image to our Azure Stack gallery.

Having an image in the gallery is also a prerequisite for deploying MSSQL as PaaS which we’ll be doing in a later guide.

We’re going to carry out this task from within the VM named MAS-CON01, so fire up an RDP session and login with the AzureStack\AzureStackAdmin user (mstsc /v:mas-con01 /f)

Once logged on, the first thing you’re going to need is a Server 2016 ISO, if you already have one of your own, you can use that. If not, you can download an evaluation HERE. When prompted, select the ISO version of the download and choose a save location.

So, we now have an ISO but we’ll still need to install PowerShell for Azure Stack and grab the Azure Stack tools from GitHub.

Install PowerShell for Azure Stack

Launch an elevated PowerShell ISE console.

Before installing this module, you’d generally want to remove older versions first, but as we’re doing this on MAS-CON01, it’s a step we can skip. For the sake of completeness though, here’s the command you would use:

Get-Module -ListAvailable | ? Name -like "Azure*" | Uninstall-Module

The PowerShell below will do the following:

  • Install the AzureRM.Bootstrapper module
  • Install the 2017-03-0-profile version of the AzureRM modules for Compute, Storage, Network, Key Vault etc.
  • Install Azure Stack specific PowerShell modules
# Install the AzureRM.Bootstrapper module
Install-Module -Name AzureRm.BootStrapper

# Install and import the API Version Profile required by Azure Stack into the current PowerShell session
Use-AzureRmProfile -Profile 2017-03-09-profile

# Install Azure Stack specific PowerShell modules
Install-Module -Name AzureStack -RequiredVersion 1.2.9

You can check you have successfully installed the required modules by running the following command:

Get-Module -ListAvailable | ? Name -like "Azure*"

The output should look something like this:


Download Azure Stack Tools from GitHub

The first thing I’m going to do here is install Git for Windows on MAS-CON01, you can download it from HERE and run through the installer accepting all the defaults.

Now close and reopen your elevated PowerShell ISE console.

To close the repository, paste in the following code and run it:

# Change directory to the root directory
cd \

# clone the repository
git clone

# Change to the tools directory
cd AzureStack-Tools

If you see the following error, you didn’t close and reopen your PowerShell console…naughty!


Alternatively, to download the tools folder using PowerShell, paste in the following code and run it:

NOTE:  If you change the download directory here, take care NOT to download it to the C:\Windows\System32 directory.

# Change directory to the root directory
cd \

# Download the tools archive
Invoke-WebRequest -OutFile

# Expand the downloaded files
Expand-Archive -DestinationPath . -Force

# Change to the tools directory
cd AzureStack-Tools-master

Create and Upload a Default Windows Image…cont.

Now we’ll need to import the Azure Stack Connect and ComputeAdmin modules and create the Azure Stack administrator’s AzureRM environment.

Import-Module .\Connect\AzureStack.Connect.psm1
Import-Module .\ComputeAdmin\AzureStack.ComputeAdmin.psm1

Add-AzureStackAzureRmEnvironment -Name "AzureStackAdmin" -ArmEndpoint "https://adminmanagement.local.azurestack.external"

Now we’ll need to get a hold of the GUID for the AAD user you specified when deploying Azure Stack, using the PowerShell below:

$TenantID = Get-DirectoryTenantID -AADTenantName "<myaadtenant>" -EnvironmentName AzureStackAdmin

The value for “myadtenant” should be your AAD Directory name In my experience, this is generally

Now we can FINALLY prepare and upload our Default Server 2016 image.

Still within the same elevated PowerShell ISE console, modify the following code for your environment and run it:

$ISOPath = "<Fully_Qualified_Path_to_ISO>"

# Store the service administrator account credentials in a variable
$UserName="<Username of the service administrator account>"
$Password="Admin password provided when deploying Azure Stack" | `
ConvertTo-SecureString -Force -AsPlainText

$Credential=New-Object PSCredential($UserName,$Password)

# Add a Windows Server 2016 Evaluation VM Image.
New-Server2016VMImage `
-ISOPath $ISOPath `
-TenantId $TenantID `
-EnvironmentName "AzureStackAdmin" `
-Net35 $True `
-AzureStackCredentials $Credential

The username required is the AAD account you used when deploying Azure Stack in this format:

NOTE:  Is you receive the “WARNING: Unable to acquire token for tenant ‘common'” message during the upload, this can be ignored.

Don’t worry if this step takes a while, especially when you see “Downloading” repeated again and again and again!

When complete you should receive the following message: “StatusCode: Created”, a little low key considering how long it makes you wait.


…aaaaaaaaaaaaaaand we’re done.

Hopefully that made things a little easier for someone out there, if not then this’ll be my documentation when I need to run through the process again.

Join me in PART 2 when I’ll be running through the deployment of MSSQL as PaaS

6 Replies to “Part 1: Deploy Azure Stack Single Node POC”

  1. Hi David,

    When I try to run this command:
    Add-AzureStackAzureRmEnvironment -Name “AzureStackAdmin” -ArmEndpoint “https://adminmanagement.local.azurestack.external”
    An error occur:
    Add-AzureStackAzureRmEnvironment : The term ‘Add-AzureStackAzureRmEnvironment’ is not recognized as the name of a
    cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify
    that the path is correct and try again.

    My AzureStack PowerShell version 1.2.11

    Please advise, thanks so much!

    Vu Pham

    1. Hi Vu Pham,

      That cmdlet was used in AzureStack PowerShell version 1.2.9, being that you’re using 1.2.11, try the following instead, I believe that’ll work for you 🙂

      Add-AzureRMEnvironment `
      -Name “AzureStackAdmin” `
      -ArmEndpoint “https://adminmanagement.local.azurestack.external”

      There have been quite a few changes since I uploaded that guide, so I’m in the process of putting up a new one which should be available in about 2 days time. Hopefully the above will get you going again though.

      Best regards,

  2. Hi David,
    I’m setting up some AZStacks for a training environment. The plan is to get everything built and then clone/snapshot a master image that could then be replicated to multiple users. The sticking point is the AAD account that the script uses at the start of the deployment. Is it possible to transfer or duplicate the stack and register it to a different AAD user with a different tenant?

    Thanks for your time.

    1. Hi Steve,

      Unfortunately I don’t think that’s a question I can answer as I’ve never had cause to try it 🙂
      Instinct says that it won’t work though, and there’s no way that I know of to change the tenant an ASDK is registered against.

      With that in mind though, it’s been a while since I’ve played with an ASDK so it might be work reaching out to the community:

      Not a lot of help I know, but I hope it gets you somewhere 🙂

      Good luck.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.