Angular 6 application hosted on Azure Storage Static website

A few days ago Microsoft announced a new public preview feature for Azure Storage called Static website. It enables you to host a static web app using Azure Storage which is multiple times cheaper then a traditional required Web App. Reason enough to give it a try.

Create a Storage account

To use the Static website feature we need a general purpose V2 Storage Account. You can either manually create the resource within the Azure Portal or by using the following ARM Template:

Enable Static website feature

Unfortunately we can’t enable the Static website feature using an ARM Template (thanks to Gaurav Mantri) because it is not part of the Storage Resource Provider API. So we have to manually enable it after we’ve provisioned the Storage Account.

We can do that within our previous created Storage Account: Select Static website (preview) to configure a container for static website hosting:


Since our example applicaton will have an index.html,  we can leave the index document name field as it is.

Note: If you have to automate this process, it is still possible to enable this feature using the Azure Storage Services REST API.

After we save our changes we get the primary endpoint URL of our site:


Create an Angular Single Page Application

We use the Angular CLI to scaffold an Angular sample applicaton that already has routing enabled. The name of our app will be spastore:

ng new spastore --routing

We also create a new component named subcomponent to demonstrate the routing capability:

ng g component subcomponent

And configure the routing module accordingly:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { SubcomponentComponent } from './subcomponent/subcomponent.component';

const routes: Routes = [
  { path: 'subcomponent', component: SubcomponentComponent },

  imports: [RouterModule.forRoot(routes, { useHash: true })],
  exports: [RouterModule]
export class AppRoutingModule { }

Note that we use the HashLocationStrategy oppsed to the default PathLocationStrategy because the later will result in 404 errors. This is because we can’t define a rewrite URL for our static website and Azure will try to resolve the specified resource – e. g.:

The HashLocationStrategy represents its state in the hash fragment of the browser’s URL. So this will be the new route for our subcomponent:

Finally we add the router outlet to the app.component.html which displays our component and add a router link:


Now it’s time to compile the application and get a production build. Once again we use the Angular CLI:

ng build --prod

The build artefacts are stored within the dist folder and are ready to publish. We can do that by clicking on the $web link within the Static website (preview) blade. Then we have to click on the Upload button and select all files within the artefact (dist) folder:


If we browse to the URL we can see our Angular App up and running and also the routing is working as expected:


You can test the site here:

The source code including the ARM Template and the Angular sample application can be found in my GitHub repository.


You can actualy use the PathLocationStrategy by setting the error page to index.html (thanks to @nthonyChu). The first try to serve a URL like will fail since it is not a valid resource. Then the path will get passed to the error page which is our index.html (page returns content with 404 status code). However, this is hack. I still recommend using the HashLocationStrategy until a rewrite mechanism is in place.

Find outdated Azure ARM QuickStart Templates on GitHub

In my previous post Determine latest API version for a resource provider I showed you how to retrieve the latest API version for a specific resource provider using the Get-AzureRmResourceProviderLatestApiVersion cmdlet. In this post I will use the cmdlet to find any outdated resource provider within an ARM template. Also, we will analyze the Azure quickstart templates on GitHub!

To analyze an ARM template we have to convert the JavaScript Object Notation (JSON) to a custom PowerShell object that has a property for each field in the JSON.
We will use the following lean template for testing purpose (download):

    "$schema": "",
    "contentVersion": "",
    "resources": [
            "type": "Microsoft.Storage/storageAccounts",
            "name": "myuniquestorageaccountname",
            "apiVersion": "2016-01-01",
            "location": "West Europe",
            "sku": {
                "name": "Standard_LRS"
            "kind": "Storage"

The first thing we have to do is to load the file using the Get-Content cmdlet and pipe the result to the ConvertFrom-Json cmdlet. To retrieve the resources we further pipe the result to the Select-Object cmdlet:

Get-Content leanArmTemplate.json |
        ConvertFrom-Json |
        Select-Object -ExpandProperty resources

This will give us all necessary information like the resource provider and the used API version:

type       : Microsoft.Storage/storageAccounts
name       : myuniquestorageaccountname
apiVersion : 2016-01-01
location   : West Europe
sku        : @{name=Standard_LRS}
kind       : Storage

Now finally to analyze the template, we have to iterate over each resource and retrieve the latest API version using the previous mentioned Get-AzureRmResourceProviderLatestApiVersion cmdlet. To get a handy output we create a new PsCustomObject containing the resource type, the used and latest API Version and a flag that specifies whether the used API version is the latest:

Get-Content leanArmTemplate.json |
        ConvertFrom-Json |
        Select-Object -ExpandProperty resources |
         ForEach-Object {
            $latestApiVersion = Get-AzureRmResourceProviderLatestApiVersion -Type $_.type
                Type = $_.type
                UsedApiVersion = $_.apiVersion
                LatestVersion = $latestApiVersion
                Latest = $_.apiVersion -eq $latestApiVersion

This is the output:

Type                              UsedApiVersion LatestVersion Latest
----                              -------------- ------------- ------
Microsoft.Storage/storageAccounts 2016-01-01     2018-02-01     False

In this example, the used API for the resource provider Microsoft.Storage/storageAccounts is not up to date.

For the sake of convenience I wrapped the code into a Get-OutdatedResourceProvider cmdlet and added some special treatment to handle nested resources:

function Get-OutdatedResourceProvider
        [Parameter(Mandatory = $true, Position = 0)]

    Get-Content $Path |
        ConvertFrom-Json |
        Select-Object -ExpandProperty resources |
        ForEach-Object {
        $latestApiVersion = Get-AzureRmResourceProviderLatestApiVersion -Type $_.type
            Type = $_.type
            UsedApiVersion = $_.apiVersion
            LatestVersion = $latestApiVersion
            Latest = $_.apiVersion -eq $latestApiVersion

        # a resource may include sub resources
        foreach ($subResource in $_.resources)
            $latestApiVersion = Get-AzureRmResourceProviderLatestApiVersion -Type ('{0}/{1}' -f $_.type, $subResource.type)
                Type = '{0}/{1}' -f $_.type, $subResource.type
                UsedApiVersion = $_.apiVersion
                LatestVersion = $_.apiVersion -eq $latestApiVersion

🕵️‍♂️🕵️‍♂️🕵️‍♂️ Now it’s time to analyze some templates  🕵️‍♂️🕵️‍♂️🕵️‍♂️.

Lets clone the whole azure-quickstart-templates Git repository:

git clone

Then we can filter all ARM templates using the Get-ChildItem cmdlet:

$armTemplates = Get-ChildItem 'azure-quickstart-templates' -Filter 'azuredeploy.json' -Recurse

To analyze them we iterate over each template and pass it to the above created Get-OutdatedResourceProvider cmdlet. Because there might be some invalid templates, we try to parse the JSON first and filter all valid templates:

$invalidTemplates = @()
$analyzedTemplates = $armTemplates |
    ForEach-Object {
    $template = $_
        $null = $template | Get-Content | ConvertFrom-Json
        Get-OutdatedResourceProvider -Path $template.FullName
        $invalidTemplates += $template

Finally we gather and output some metrics:

$validAnalyzes = $analyzedTemplates | Where-Object LatestVersion
$uptodateproviderCount = $validAnalyzes | Where-Object LatestVersion -eq TRUE | Measure-Object | Select-Object -ExpandProperty Count
$outdatedproviderCount = $validAnalyzes | Where-Object LatestVersion -ne TRUE | Measure-Object | Select-Object -ExpandProperty Count

Write-Host "Found $($armTemplates.Count) ARM templates in the Azure quickstart repository ($($invalidTemplates.Count) of them are invalid).
They are using $($analyzedTemplates.Count) resource providers. I was able to determine the latest version for $($validAnalyzes.count) resource provider. $uptodateproviderCount of them are using the latest API version whereas $outdatedproviderCount are using an outdated API." -ForegroundColor Cyan

Here is the output:

Found 675 ARM templates in the Azure quickstart repository (3 of them are invalid).
They are using 3873 resource providers. I was able to determine the latest version for 3288 resource provider. 18 of them are using the latest API version whereas 3270 are using an outdated API.

Note: There is no need to always use the latest API version. You should consider upgrading only if you encounter issues with the resource provider or if you want to use new features that are not part of the currently used API.

You can download the complete script and run it for yourself here.

Determine latest API version for a resource provider

Azure Resource Manager templates are great to deploy one or more resources to a resource group.

A mandatory part of an ARM template is the resources section which defines the resource types that are deployed or updated. Here is an example:

"type": "Microsoft.Storage/storageAccounts",
"name": "[variables('storageAccountName')]",
"apiVersion": "2016-01-01",
"location": "[parameters('location')]",
"sku": {
	"name": "Standard_LRS"
"kind": "Storage",
"properties": {}

The name of a resource type has the following format: {resource-provider}/{resource-type}. In this example Microsoft.Storage/storageAccounts. The apiVersion specifies the version of the API that is used to deploy / manage the resource.

A resource provider offers a set of operations to deploy and manage resources using a REST API. If a resource provider adds new features, it releases a new version of the API. Therefore, if you have to utilize new features you may have to use the latest API Version. To determine the latest version of a resource type you can use the following Get-AzureRmResourceProviderLatestApiVersion cmdlet:

function Get-AzureRmResourceProviderLatestApiVersion
        [Parameter(ParameterSetName = 'Full', Mandatory = $true, Position = 0)]

        [Parameter(ParameterSetName = 'ProviderAndType', Mandatory = $true, Position = 0)]

        [Parameter(ParameterSetName = 'ProviderAndType', Mandatory = $true, Position = 1)]


    # retrieving the resource providers is time consuming therefore we store
    # them in a script variable to accelerate subsequent requests.
    if (-not $script:resourceProvider)
        $script:resourceProvider = Get-AzureRmResourceProvider   

    if ($PSCmdlet.ParameterSetName -eq 'Full')
        $ResourceProvider = ($Type -replace "\/.*")
        $ResourceType = ($Type -replace ".*?\/(.+)", '$1')
    $provider = $script:resourceProvider | 
        Where-Object {
        $_.ProviderNamespace -eq $ResourceProvider -and 
        $_.ResourceTypes.ResourceTypeName -eq $ResourceType

    if ($IncludePreview)
        $provider.ResourceTypes.ApiVersions | Where-Object {
            $_ -notmatch '-preview'
        } | Select-Object -First 1

You can now determine the latest API Version using either the full type:

Get-AzureRmResourceProviderLatestApiVersion -Type Microsoft.Storage/storageAccounts

Or by passing the Resource Provider and Resource Type:

Get-AzureRmResourceProviderLatestApiVersion -ResourceProvider Microsoft.Storage -ResourceType storageAccounts

Finally, to include preview versions you can append the -IncludePreview switch:

Get-AzureRmResourceProviderLatestApiVersion -Type Microsoft.Storage/storageAccounts -IncludePreview

You can download the script from my GitHub repository.

Additional information: Resource providers and types

Things you may consider when choosing a Microsoft Azure Region.

With over 50 regions worldwide, Azure has more global regions than any other cloud provider. When you have to choose a region for your Azure resources, consider these four factors:

  1. Not all services are available in all regions
    You have to ensure all resources you want to deploy are available within the desired region. Here is a nice overview of Products available by region.
  2. Prices vary by region
    Not all Azure Regions have the same pricing for the same resources. While most of the time you are looking for the closest Azure Region to your company / customers, you may consider choosing another Region due to the lower pricing model. A good resource to determine the price for a resource for a specific region is the Azure Pricing Calculator.
  3. Latency from your customer to the Azure datacenter
    For performance reason you want to choose the Azure region with the least latency to your company / customers. To measure the latency you can perform a Azure Latency Test on
  4. Location of your customer data (data residency)
    You may have specific compliance or data-residency requirements that will force you to use a specific region. Get data residency details.

Additional information: Learn more about Azure Regions 


Serving a HTML Page from Azure PowerShell Function

The new PowerShell language (experimental) support in Azure Function is really handy. Especially if you want to use the Azure PowerShell cmdlets to retrieve any kind of Azure Resources and diplay them in a HTML page. Hosting the cmdlet in Azure Function eliminates the need of a local installed Azure PowerShell module.

Here is a simple PowerShell Function that returns a Hello world HTML page:

# POST method: $req
$requestBody = Get-Content $req -Raw | ConvertFrom-Json
$name = $

# GET method: each querystring parameter is its own variable
if ($req_query_name)
    $name = $req_query_name

$html = @'

<header>This is title</header>

Hello world


Out-File -Encoding Ascii -FilePath $res -inputObject $html

Invoking the script in a browser doesn’t give us the desired result. The content gets interpreted as XML instead of HTML:


The reason for that is that the Content-Type is set to applicaton/xml:


The output of an Azure PowerShell function is a file (called $res by default) – so how can we change the content type to text/html?

It turns out (thanks to Mikhail) that we can construct a Repsonse Object using a JSON string (here the node definition) where we can set the content and the content type:

$resp = [string]::Format('{{ "status": 200, "body": "{0}", "headers": {{
"content-type": "text/html" }} }}', $html)
Out-File -Encoding Ascii -FilePath $res -inputObject $resp

If we invoke the function in the browser again we get the desired result:

And the content-type is set to text/html:

Note that if your HTML contains JSON characters like double quotes or backslashes you will have to escape them. Example:

$html -replace '"', '\"'

Three reasons why you should associate multiple subscriptions with the same Azure Active Directory

In Azure, multiple subscriptions can trust the same Azure Active Directory but each subscription trusts only one directory.

If you create a new Azure subcription, a new Azure Active Directory is automatically created and associated with your subscription. To provide a user access for a resource you can use Role-Based Access Control (RBAC) given that the user is part of the associated Azure Active Directory. You can also add existing users from another Azure Active Directory as guest but I would still recommend to link your subscriptions with the same directory for the following three reasons:

  1. If you use a different directory for your subscription you won’t be able to move resources between your subscription:

    The source and destination subscriptions must exist within the same Azure Active Directory tenant.

  2. You can easy jump to your resources using the “All resources” blade by using the “Filter by Name” search field and don’t have to remember which resource belongs to which subscription:
  3. If your user is a guest in many directories, your tenant list will grow and switching directories will become a mess:


Read here how to associate or add an Azure subscription to Azure Active Directory

Configure Azure Cloud Shell to use a profile hosted on GitHub

You may have noticed that you can run the Azure Cloud Shell without the portal as a separate component on

The shell is really handy since it can be used from everywhere. Today I want to show you how you can load a remote profile that is hosted on GitHub in the Azure Cloud Shell.

A PowerShell profile is used to add aliases, functions or variables to a session every time you start the shell.

The Azure Cloud Shell uses a fileshare stored on your storage account to persist files. This is also true for your profile. You can determine your profile path by entering:


You will see a path similar to this:


This doesn’t mean that the profile exists, its just the path where Azure Cloud Shell tries to load your profile when you start it. You can determine whether the file actually exists using the Test-Path cmdlet:

Test-Path $profile

The cmdlet should return false if you didn’t already created a profile:


You could create a profile using the New-Item cmdlet and go to your file share to edit it. But you may like to have a history where you can compare the changes you made. You may also want to use the same profile for different accounts. So how can we connect a profile that is stored in a GitHub repository?

Lets start with adding the actual profile to our GitHub repository. My profile.ps1 contains a single function to print Hello World:

function Show-HelloWorld
    Write-Host "hello, world!"

Next we have to load the profile. For that purpose I have created another file called Set-Profile.ps1:

$profilePath = ''

$downloadString = '{0}?{1}' -f $profilePath, (New-Guid)
Invoke-Expression ((New-Object System.Net.WebClient).DownloadString($profilePath))

The $profilePath contains the URL to the previous created profile.ps1. I do append a query string to the path containg a random guid to prevent the web client from caching the file. This is particularly usefull when we update the profile.ps1 in the GitHub repository and want to load these changes without restarting the shell by dot sourcing the profile.
In line 4,  I download the profile.ps1 as a string and execute it using the Invoke-Expression cmdlet to load it to the runspace.

The last step we need to do is to set the content of the Set-Profile.ps1 to the actual PowerShell profile. We can do this by executing the following snippet in the Azure Cloud Shell:

(New-Object System.Net.WebClient).DownloadString('') |
  Set-Content $profile -Force

The snippet is using the web client again but instead of executing the code,  it pipes the string to the Set-Content cmdlet to override the profile. I can verfiy that by retrieving the content of $profile. This should output the content of my Set-Profile.ps1:


Finally to load the profile we can either restart the PowerShell session or dot source the profile as mentioned earlier:

. $profile

And now we can use all the aliases, variables and function we have defined in the profile that is stored on GitHub: