Essential tips for building a large Azure blueprint

Azure Blueprint allows an organization to design and build a standardized and repeatable cloud templates in Azure that meet pre-defined reference architecture as well as corporate compliance and policy for cloud deployment.

Building a simple blueprint shouldn’t take time. However in a real-world scenario, as well as when you work for large organization you may need to build a complex blueprint that require multiple dependencies. This article is going to give you key essential tips to help you simplify building a large and complex Azure blueprint.

Logical Architecture

A logical architecture would be just a diagram showing which resources you are going to deploy in Azure.  The diagram shows you the deployment of Windows virtual machine along with different services such as Virtual Network, Azure Bastion, Storage Account, Log Analytics and Key Vault. This one is not a complex deployment but it would give you an idea how a logical architecture diagram would look like.

With logical architecture diagram you may have an idea on what need to be deployed first. In the above example we see that prior to deploying a virtual machine an Azure virtual network with other network resource types should be ready.

Dependency Map

Dependency map is simply a map showing you what are dependencies of each logical component if any. Continue to use the example above:

  • Virtual network is a dependency of virtual machine, Azure bastion service.
  • Log Analytics workspace is a dependency of virtual machine log.
  • Key Vault is a dependency of virtual machine disk encryption

Depending on how much of detail you would like, the dependency level may vary. In some cases you may need be aware of non-dependency resource in order to resolve cloud resource deployment conflict. For example if your virtual machine has multiple script extension resource type, you may need to set a dependency to ensure no conflict may occur during the deployment.

As a cloud architect, you may need to draw a dependency map. Below is a sample one:

Sample dependency – all logs are written to a single log analytics workspace

Dependency mapping will help you figure out what you should put in dependOn element in your artifact and blueprint definition.

Blueprint As Code

For small and simple blueprint, you can use Azure Portal to create one (see this example). However, advanced or large blueprint couldn’t be achieved with Azure Portal especially when you need global parameters (e.g. Project Code, Project Stage or Cutsom Tag) to follow your own standard naming convention.

Another reason why blueprint as code is recommended is to support building a CICD pipeline for your blueprint. Managing blueprint as code will help simplify the development and deployment.

Global Parameter

When it comes to building a blueprint that follows reference architecture, there are usually global parameters. One of the very common ones are naming standard convention. You would like your resource name to be followed like {project_code}-{project_stage}-{service_name} . With global parameter you can resuse in individual artifact. This example below gives you an idea on how a global parameter look like. In the example we see tagging, project code, project stage, resource group location and log retention parameters and their values are applied to all resource artifact.

Note that global parameter is different from local parameters. Global parameter is the one that are used in one more/all artifacts while local parameters are used specifically for an artifact.

You should  create a spreadsheet of global parameters that you may think your blueprint use. It ideally has the following info:

  • Parameter Name
  • Display Name
  • Description
  • Global/Local (Yes/No)
  • Optional: Artifact (Use in which of artifact if not a global parameter)

Naming Global Parameter

Global parameter name should be different from artifact parameter. This can help distinguish between the two types of parameters and you won’t be confused when using them. See the example below in an artifact in which artifact parameter value is referenced by global parameter.

"parameters": {
  "resourceTags": {
    "value": "[parameters('g_resourceTags')]"
  },
  "projectCode": {
    "value": "[parameters('g_projectCode')]"
  },
  "projectStage": {
    "value": "[parameters('g_projectStage')]"
  },
}

In the example, we put a letter ‘g‘ as a prefix in global parameter (g stands for global).

Az.Blueprint Module

Given the fact that you can build your own module to be used within your pipeline or inside your development team. However I’d recommend you to try Az.Blueprint first before cooking an own one. The module provides you common operations such as creating a new blueprint, getting a blueprint, importing an artifact or so on.

The only note with the use of Az.Blueprint is that you would need to follow some standards. For example when using Import-AzBlueprintWithArtifact   your blueprint directory need to follow some rules (e.g. all artifact templates must be stored in a directory named artifacts). To understand more details on how this module works behind the scene you can decompile the module DLL and read the code. Below is decompiled function named GetValidatedFilePathForBlueprint() that gives you an idea on what it does does – finding blueprint.json in your directory.

protected string GetValidatedFilePathForBlueprint(string path)
{
    string path2 = ((IEnumerable<string>) AzureSession.get_Instance().get_DataStore().GetFiles(path, "*.*", SearchOption.TopDirectoryOnly)).Select<string, string>((Func<string, string>) (file => Path.GetFileName(file))).FirstOrDefault<string>((Func<string, bool>) (name => string.Equals(name, "blueprint.json", StringComparison.OrdinalIgnoreCase)));
    if (path2 == null)
    throw new Exception("Cannot locate Blueprint.json in: " + path + ".");
    return Path.Combine(path, path2);
}

There is still a limitation though. You would have to put everything in one single blueprint.json file. That said managing a long list of parameters would be kind of painful. You may need an enhancement to read each blueprint for each artifact so you could manage local artifact parameter easier.

Boilerplate template

Before putting your hands on writing template definition for your blueprint you should create a boilerplate template to ensure it follows blueprint standard as well as to avoid errors when using Az.Blueprint module.

For artifact template:

{
    "kind": "template",
    "id": "/providers/Microsoft.Blueprint/blueprints/azsec_foundation/artifacts/loganalytics_blueprint",
    "type": "Microsoft.Blueprint/blueprints/artifacts",
    "name": "loganalytics_blueprint",
    "properties": {
        "resourceGroup": "projectResourceGroup",
        "displayName": "Log Analytics template",
        "description": "A blueprint to deploy Log Analytics",
        "template": {
            "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
            "contentVersion": "1.0.0.0",
            "parameters": {

            },
            "variables": {

            },
            "resources": [

            ],
            "outputs": {

            }
        },
        "parameters": {
            
        },
        "dependsOn": [
            
        ]
    }
}

For blueprint definition template:

{
    "id": "/providers/Microsoft.Blueprint/blueprints/azsec_foundation",
    "type": "Microsoft.Blueprint/blueprints",
    "name": "azsec_foundation",
    "properties": {
        "targetScope": "subscription",
        "displayName": "AzSec Foundation Blueprint",
        "description": "AzSec Foundation Blueprint provides foundation templates for a new project",
        "parameters": {

        },
        "resourceGroups": {
            
        }
    }
}

For assignment template:

{
    "identity": {

    },
    "location": "",
    "properties": {
        "blueprintId": "subscriptions/61a6179d-bb2d-4ccd-8c56-4d3ff2e13119/providers/Microsoft.Blueprint/blueprints/azsec_foundation",
        "resourceGroups": {

        },
        "parameters": {

        }
    }
}

You can get the template from here.

Azure ARM Validation

This is extremely important. First, everything inside template element in artifact template is actually things that you normally see in azuredeploy.json when deploying Azure ARM template. The problem with Azure Blueprint is that during the assignment it may give you an unclear information if the deployment of an artifact is failed. Tracing in Azure Activity Log may give nothing if the error is part of the artifact template itself (e.g. unsupported value in a parameter or missing required resource property).

Before blueprint assignment, you should deploy your artifact independently. Give artifact’s parameters default values then validate (Test-AzResourceGroupDeployment  and New-AzResourceGroupDeployment  should help) and try to deploy in a resource group to see if the deployment is succeeded.

secureString Consideration

If you intend to use secureString for your parameter here is what you should know. By default, when you define a secureString, Azure Blueprints will enforce it to be a value stored in Azure Key Vault. If you do blueprint assignment from Azure Portal it may give you a surprise. To solve the issue simply define assignment parameter as code, sample is as follows

"g_vmLocalAdminPassword": {
    "reference": {
        "keyVault": {
            "id": "/subscriptions/aa2201b1-a52a-1199-11ec-930c4deb35e8/resourceGroups/azsec-rg/providers/Microsoft.KeyVault/vaults/shared-kv"
        },
        "secretName": "vmLocalAdminPassword"
    }
}

Conclusion

Building a complex Azure blueprint does require a plan, as well as an architecture diagram and artifact blueprint document. If you don’t have a systematic approach you would have to refactor your blueprint which leads to time consuming and effort.

Below are helpful references that can be used along with this article:

This entry was posted in Governance & Compliance and tagged , . Bookmark the permalink.

8 Responses to Essential tips for building a large Azure blueprint

  1. alan-msft says:

    Great article. This is very helpful for some customers I’m working to help architect Azure blueprint. Thank you very much for sharing experience.

  2. Anh Le says:

    I’m trying to understand how to set values for local (artifact) parameters during Blueprint assignment. Do you have any article for it?

    • azsec says:

      All values are stored in a file you can call assign.json. https://github.com/azsec/azure-blueprints/blob/master/EnterpriseApp/App-SQL/assign.json

      You can find the sample of blueprint that deploys Azure App Service with Azure SQL Service from this link https://github.com/azsec/azure-blueprints/tree/master/EnterpriseApp

      Let me know if you still encounter or have any question.

      • Anh Le says:

        Thanks for quick reply. I was trying to confirm that we need to put values for both artifact and blueprint parameters into a single assign.json file and the use the following to “import” the values from the assign.json into artifact parameters:

        “parameters”: {
        “resourceTags”: {
        “value”: “[parameters(‘g_resourceTags’)]”
        },
        “projectCode”: {
        “value”: “[parameters(‘g_projectCode’)]”
        },
        “projectStage”: {
        “value”: “[parameters(‘g_projectStage’)]”
        },
        “storageAccountSku”: {
        “value”: “[parameters(‘g_storageAccountSku’)]”
        },
        “storageAccountType”: {
        “value”: “[parameters(‘g_storageAccountType’)]”
        },
        “advancedThreatProtectionEnabled”: {
        “value”: “[parameters(‘g_advancedThreatProtectionEnabled’)]”
        }
        }

        This is kinda tedious because with blueprint parameters, we can just use the parameters directly as below

        blueprint.json

        “parameters”: {
        “principalIds”: {
        “type”: “string”,
        “metadata”: {
        “displayName”: “Principal IDs”,
        “description”: “This is a blueprint parameter that any artifact can reference. We’ll display these descriptions for you in the info bubble. Supply principal IDs for the users, groups or service principals for the RBAC assignment”,
        “strongType”: “PrincipalId”
        }
        },
        “genericBlueprintParameter”: {
        “type”: “string”,
        “defaultValue”: “MyDefaultParamValue”
        }
        }

        roleAssignment.json
        {
        “kind”: “roleAssignment”,
        “properties”: {
        “roleDefinitionId”: “/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635”,
        “principalIds”: [“[parameters(‘principalIds’)]”],
        “displayName”: ” : Owner”
        },
        “type”: “Microsoft.Blueprint/blueprints/artifacts”,
        “name”: “rbacAssignment”
        }

        https://github.com/Azure/azure-blueprints/tree/master/samples/101-boilerplate

        • azsec says:

          roleAssignment is a special type which cannot be set dyanmically. Key Vault either.

          Assign.json is where the actual value for each blueprint parameter. Blueprint definition template is where all parameters that artifacts need to reference to. As explained in my article, this kind of structure can only be used with Az.Blueprint PowerShell module because Microsoft designs for its own purpose.

          • Anh says:

            I think my source of confusion is how Az.Blueprint is designed. Even though Blueprints have blueprint and artifact parameters, to set values to artifact params, we have to also define them in the blueprint.json as blueprint params which are then parsed to artifact params
            https://www.wesleyhaakman.org/content/images/2019/03/Flow.png

          • azsec says:

            As you can see my previous comment, blueprint.json contains all parameters that are used in all artifacts. In each individual artifact parameter is referenced to the blueprint definition. The value of blueprint parameter is declared in assign.json. This concept is very much like Azure ARM template. There is not anywhere else that blueprint parameter is set (unless you use defaultValues in parameter). You can see in the screenshot, webapp.json has two parameters and at the end of it, in parameters element these artifact parameters need to reference to the blueprint parameter. That is why in my article I suggested to put g_ as prefix in blueprint parameter in order to avoid confusion. Here is a sample https://github.com/azsec/azure-blueprints/blob/master/EnterpriseApp/App-SQL/artifacts/artifact.app.json#L179

            Local blueprint parameter in my article really means that this type of parameter is used only in one artifact. While global parameter means it is represent in more than one artifact. See resourceTag, projectCode and projectStage I put in every artifact.

          • Anh says:

            Thanks a lot for the clarification. I couldn’t find it on Microsoft Docs.

Leave a Reply