7 min read

Preparing Azure Lighthouse customer subscriptions for Azure Blueprints

Preparing Azure Lighthouse customer subscriptions for Azure Blueprints

Over the the past few months Azure Blueprints took some leaps and became much easier to use. With a growing list of Blueprint samples and the PowerShell module, Azure Blueprints is one of the go-to resources when it comes to Azure Governance. Additionally, Azure Blueprints is highlighted within the Cloud Adoption Framework as a great way to build a landing zone for new environments. Can't argue with that :)

A good question by Martin Ehrnst last week about onboarding using Azure Lighthouse and applying governance last week got me thinking.

Even though there is a bit of a learning curve, I see a lot of potential in using Azure Blueprints and especially in combination with Azure DevOps. But using Azure Blueprints in combination with Azure Lighthouse does come with some requirements and doesn't work out-of-the-box. If you're going with the default Azure Lighthouse examples and going for the "next-next-finish see if this works" approach. You might be faced with an error stating that your Blueprint cannot be assigned.

However, it is possible to get up and running without too much effort. But, this is something you want to configure firs thing when onboarding a new subscription as you generally don't want to go back and forth asking the subscription owner to perform some actions for you.

I've written about detecting when access is delegated to you and automating deployments based on that notification (here). But what if you want to automatically deploy Blueprints based on that notification? Let's see how you could go about getting that done.

The challenge

As you can currently only delegate permissions up to the level of Contributor you will face some challenges here. Creating a draft and publishing the Blueprint to the delegated subscription will work as expected. But assigning the Blueprint and using a "System Assigned Managed Identity" will leave you with a one of two error messages stating you don't have sufficient permissions or Azure Blueprints was unable to obtain owner permissions for the subscription.

Let's assume you have been granted permissions to write Azure Blueprints (which is not that likely but more on this later). Your next option would be to go with a "user assigned managed identity". If you have contributor level access, you have sufficient permissions to create one. Once you've created the user assigned managed identity you can have another go at assigning the Blueprint.

At first you'll receive a popup stating"Blueprint assignment succeeded". Awesome  - tears of joy, cheering, balloons! But after inspecting the deployment you will find it didn't go so well.

The actual Blueprint assignment succeeded. But, the deployment failed. We didn't configure permissions for the user assigned managed service identity. And here's the catch.. With Contributor level access you can't assign the appropriate permissions to make this work.

After assigning the appropriate permissions to the user assigned managed identity on the subscription, the Blueprint will assign successfully. The problem here is that after onboarding your subscription / customer with Azure Lighthouse you don't necessarily want to go back and ask the subscription owner to perform these actions for you.

The solution

As deploying the Azure Lighthouse Managed Services Resources already requires owner permissions on the subscription - why not use that initial deployment to prepare the subscription so you can use Azure Blueprints to manage the environment? In reality, when engaging with a new customer there probably is going to be a moment where you can perform these actions. If your deploying the subscriptions and the managed services templates yourself, there's no reason why you shouldn't be able to do this as well.

There are a few things we need for this to succeeded.

Permissions
Last month a new built-in role "Blueprint Operator" was released. This role will let you assign Blueprints, sounds like something we can use. Note that using this role also requires the "Managed Identity Operator" role. Additionally we need the Contributor role to save and publish Blueprints and perform other management activities on the subscription.

Managed Service Identity
We need to create a user assigned managed service identity and grant it the permissions we need to manage the subscription using Azure Blueprints.

Azure Lighthouse Templates
Because we don't want to go back and forth requesting the correct permissions or having the customer deploy resources for us we want to deploy everything in one go.

Because we initially need owner access to the subscription anyway (or have the customer perform the onboarding), I went with some PowerShell that performs the required actions to ready the environment for use with Blueprints and onboard using Delegated Resource Management.

First we need the Role Defintion Id's for the "Blueprint Operator" and "Managed Identity Operator" that we can use in our Lighthouse template. We can retrieve them using the Az PowerShell commands:

Wesley @ Azure\Lighthouse> (Get-AzRoleDefinition -Name "Blueprint Operator").Id
437d2ced-4a38-4302-8479-ed2bcb43d090
Wesley @ Azure\Lighthouse> (Get-AzRoleDefinition -Name "Managed Identity Operator").Id
f1a07417-d97a-45cb-824c-7a7467783830
Wesley @ Azure\Lighthouse>

We then need to add these ID's to the Azure Lighthouse Template (example here). To do so, edit the parameters file (or parse the parameters when deploying) and make sure the following values are present:

authorizations": {
            "value": [
                {
                    "principalId": "<ID of your user/group/service principal>",
                    "principalIdDisplayName": "Name - Contributor",
                    "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c"
                },
                {
                    "principalId": "<ID of your user/group/service principal>",
                    "principalIdDisplayName": "Name - Blueprint Operator",
                    "roleDefinitionId": "437d2ced-4a38-4302-8479-ed2bcb43d090"
                },
                {
                    "principalId": "<ID of your user/group/service principal>",
                    "principalIdDisplayName": "Name - Managed Identity Operator",
                    "roleDefinitionId": "f1a07417-d97a-45cb-824c-7a7467783830"
                }

            ]
        }

You most likely want to provide a more logical "principalIdDisplayName" as you can already see the name of the role in the "My Customers - Delegations" screen but for demo purposes, this will do.

Next what you'll need is the Az.ManagedServiceIdentity module with support for User Assigned Identities. This requires you to install the pre-release version of the module.

Install-Module -Name PowerShellGet -AllowPrerelease
Install-Module -Name Az.ManagedServiceIdentity -AllowPrerelease

(https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/how-to-manage-ua-identity-powershell).

For the actual onboarding we can now go with a PowerShell script to perform the actions in sequence.

Onboarding

The script to onboard the customer is pretty straight forward. First we'll create a Resourcce Group to deploy the Managed  Identity in. Once provisioned we can deploy the Managed Identity and assign it the appropriate permissions. I'm going with owner here but you can go with whatever you prefer. Even though I'm not a fan of introducing "sleep" or "waits" into a script, to achieve our goal we need to. As it takes a few seconds for the identity to be registered within the identity we need to give Azure some time to do this. Removing the start-sleep will result in a message stating something similar to "Principal X does not exist in directory Y". You could go with a little less time but I found 10 seconds works best and is a safe bet.

$uamiRG = "rg-Lighthouse-uami"
$location = "west europe"
$subscriptionId = (get-azcontext).subscription.id

$lighthouseTemplate = ".\ManagedServicesDeployment.json"
$lighthouseTemplateParameters = ".\ManagedServicesDeployment.parameters.json"

New-AzResourceGroup -name $msiRG -location $location

$uami = New-AzUserAssignedIdentity -ResourceGroupName $uamiRG -Name "lighthouse-uami"
Start-Sleep -Seconds 10
New-AzRoleAssignment -ObjectId $uami.PrincipalId -RoleDefinitionName "Owner" -Scope /subscriptions/$subscriptionId

Last but not least we'll deploy the Lighthouse template to the subscription with the parameter file where we added the new Role Definitions.

New-AzDeployment -name lighthouseOnboarding -Location $location -TemplateFile $lighthouseTemplate -TemplateParameterFile $lighthouseTemplateParameters -verbose

Running the script will result in the deployment of the User Assigned Managed Identity, the Role Assignment of "Owner" on the subscription for the Managed Identity and the delegation of access to the managing tenant.

We now have the permissions to assign Blueprints and a user assigned managed identity with owner permissions on the subscription. This should be sufficient to deploy our Blueprints to the target subscription.

We were already able to save a draft and publish a Blueprint with the Contributor permissions. Assigning is where we had a challenge. When assigning the Blueprint, select the previously deployed managed identity and configure the parameters to your liking. Assign the Blueprint and all should be well :)

You now have the access and ability to deploy and assign Blueprints from your managing tenant. Which means you can also automate the process and make it a part of your onboarding strategy. What's next? Manage the Blueprints using Azure DevOps! :)

This is one way to go about it. You could also opt to do everything through ARM templates as you are already using the templates for Delegated Resource Management. You can build upon those by adding the resources illustrated in this post to your ARM Template setup. Might do a write-up on that soon :)