Contents

Azure

Azure Blueprints Is Retiring, and I Never Got to Use It

Microsoft's retirement mail for Azure Blueprints landed in my inbox, for a subscription I was sure I never used Blueprints on. Here's how I found out what was really there, exported and cleaned it up, what really stops working on January 31, 2027, and what I'd use instead today.

On Friday evening a mail from Microsoft Azure came in: “Retirement notice: Migrate from Azure Blueprints by 31 January 2027”. It came through my own Service Health alert on my demo subscription, which surprised me: as far as I remembered, I never got round to using Blueprints.

I covered the new retirement timeline in Azure in the first half of 2026 already, so this isn’t a rehash of the dates. This one is more personal: why the mail reached me at all, what really stops working at the end of January, and what I’d use if I were building this today.

When Blueprints showed up in preview, I loved the idea. What appealed to me was a sort of desired state configuration for a whole stack: policy, resources, role assignments, the lot. Instead of setting them all up separately, you described the entire solution once, versioned it, assigned it to a subscription, and locked it so nobody could quietly change it afterwards.

Around that time my own work moved from cloud infrastructure (Azure) to cloud endpoint management (Intune), so I never really dove into it.

Blueprints itself never left preview (it’s still “Azure Blueprints (Preview)” on its retirement page), and the pieces it bundled grew up on their own: Bicep, template specs, deployment stacks, policy as code. Microsoft announced the retirement in September 2023, originally for July 2026, and later extended it to January 31, 2027, phased.

The phases from the retirement page:

DateWhat stops
July 31, 2026Creating new blueprint definitions and versions
October 31, 2026Changing existing definitions, and creating new assignments
December 31, 2026Changing existing assignments
January 31, 2027Everything: the API stops responding, Azure PowerShell and CLI commands stop working, Blueprints disappears from the portal

As of today you can still read, export and delete. After December 31 you can’t change an assignment anymore, which matters for the locks (more on that below).

The mail says you get it “because you’re associated with one or more Azure subscriptions that currently use Azure Blueprints”. It’s also in the portal under Service Health > Health advisories with tracking ID GJFQ-M1Z, which is how it reached me: my Service Health alert sends anything for that subscription to my ALERT action group. (If you don’t have that alerting in place yet, my policy-based alerting post covers how I set it up.)

So I went looking. Three places tell you what you have:

  • Blueprint definitions you can see from the subscription, including ones saved at a management group above it.
  • Blueprint assignments on the subscription.
  • The Azure Advisor retirement recommendation, which is what the View Impacted Blueprints Resources link in the mail opens.

Az.Blueprint isn’t part of the Az module, so you install it on its own. The core of my check, the full script also queries Advisor through Resource Graph:

Install-PSResource -Name Az.Blueprint -Scope CurrentUser

$sub = Get-AzSubscription -SubscriptionName 'DemoSubcription'

# Definitions, including ones inherited from management groups
Get-AzBlueprint -SubscriptionId $sub.Id

# Assignments on the subscription itself
Get-AzBlueprintAssignment -SubscriptionId $sub.Id

This is what came back (subscription IDs masked):

== Blueprint definitions visible from DemoSubcription
Name             Scope                              Versions
----             -----                              --------
DefaultBlueprint /subscriptions/<this-subscription> 1.1, 1.0

== Blueprint assignments on DemoSubcription
none

== Advisor retirement recommendations for DemoSubcription
Feature  : Azure Blueprints
Retires  : 2027-01-31
Impacted : 1.1
Resource : /subscriptions/<this-subscription>/providers/microsoft.blueprint/blueprints/defaultblueprint/versions/1.1
Problem  : Retirement notice: migrate from Azure Blueprints by January 31, 2027

Feature  : Azure Blueprints
Retires  : 2027-01-31
Impacted : 1.0
Resource : /subscriptions/<this-subscription>/providers/microsoft.blueprint/blueprints/defaultblueprint/versions/1.0
Problem  : Retirement notice: migrate from Azure Blueprints by January 31, 2027

Feature  : Azure Blueprints
Retires  : 2027-01-31
Impacted : defaultblueprint
Resource : /subscriptions/<this-subscription>/providers/microsoft.blueprint/blueprints/defaultblueprint
Problem  : Retirement notice: migrate from Azure Blueprints by January 31, 2027

So the mail was right after all. There’s a blueprint definition called DefaultBlueprint saved on the subscription itself, with two published versions and no assignment. Never assigned, so it never deployed or locked anything, but it still counts: Advisor lists the definition and each version separately, and that’s what triggers the mail.

That has to be me trying Blueprints out back when it came out and then forgetting about it. My first look in the portal turned up nothing, but it is there: under Blueprint definitions, at the subscription scope.

It also explains the number of mails. I got four, but that’s down to my setup: I have two contact addresses configured, and each one got a mail naming the subscription plus a generic “you’re associated with” one. All four are about this one forgotten definition.

Before deleting anything, I wanted to know what I’d built. Export-AzBlueprintWithArtifact writes a definition to disk as JSON, one version at a time, so my export script loops over both versions and lists the artifacts in each. For version 1.1:

== Artifacts in version 1.1
Artifact                             Kind             Name
--------                             ----             ----
1acbb8ed-74e5-4615-aa69-a10440acd3e9 policyAssignment Audit Windows VMs with a pending reboot
24c7333f-e631-4d04-9d3f-db8e600fa736 template         Azure Security Center
482d68a5-b046-4299-9612-e22985bc2693 template         Deploy KeyVault
482d68a5-b046-4299-9612-e22985bc2e42 template         Deploy Log Analytics
4a23ee75-8d81-469e-aab2-9daf22f8e5a0 policyAssignment PPC Tagging
4a23ee75-8d81-469e-aab2-9daf22f8ed48 policyAssignment PPC Tagging
51dc0286-1a33-46e6-b6b0-f1e64f5ee0c4 policyAssignment Allowed storage account SKUs
51dc0286-1a33-46e6-b6b0-f1e64f5ee873 policyAssignment Allowed virtual machine SKUs
53448be7-6f38-405b-9a72-f8a430b21df8 policyAssignment PPC Tagging
6ddb76f3-83e7-4ceb-bb04-b6053a4272a4 policyAssignment Subnets should be associated with a Network Security Group
6ddb76f3-83e7-4ceb-bb04-b6053a427a53 policyAssignment Deploy Diagnostic Settings for Key Vault to Log Analytics workspace
92c6a60c-fdd3-4a45-84d8-d2b445d185f2 template         Azure Automation Account
945e6749-98af-4d2e-9d43-2b2e591149b2 policyAssignment Diagnostic logs in Key Vault should be enabled
9dfbd43f-ef49-4c28-9bb8-a27354798023 policyAssignment Not allowed resource types
b37aa2dd-d4ba-4506-91ac-84fab5fee165 policyAssignment Audit VMs that do not use managed disks
b4ec4832-d4e2-4a3c-8cfa-a2a1875e7af5 policyAssignment Secure transfer to storage accounts should be enabled
c625e1fb-1aa4-44c0-a750-c3bcba77c488 policyAssignment Audit resource location matches resource group location
c625e1fb-1aa4-44c0-a750-c3bcba77cc3a policyAssignment There should be more than one owner assigned to your subscription
dd16a1aa-67b8-4d1d-9d90-f95259ab552a policyAssignment Allowed locations
dd16a1aa-67b8-4d1d-9d90-f95259ab5cd9 policyAssignment Allowed locations for resource groups

Not a toy, then. Its own description calls it a “Default Blueprint for an Azure environment”: three resource groups (RG-PPC-LogAnalytics, RG-PPC-AzureAutomation and RG-PPC-KeyVault, all in West Europe), ARM templates for Security Center, Log Analytics, Key Vault and an Automation account, and sixteen policy assignments for locations, SKUs, diagnostics and tagging. That’s pretty much what a landing zone would give you today.

The difference between the versions is the tagging. Version 1.0 had four separate “Append tag” policy assignments; in 1.1 those became one custom initiative, PPC Tagging, assigned to each of the three resource groups. That initiative lives on the subscription, outside the blueprint, so the export alone doesn’t bring it back. A third script checks every custom policy object the export points to, and looks for anything else with “PPC” in the name:

== Custom policy objects the exported blueprint points to
Name        Kind       Versions Scope                              Exists
----        ----       -------- -----                              ------
PPC Tagging initiative 1.1      /subscriptions/<this-subscription>  False

== Custom policy objects visible from DemoSubcription with 'PPC' in the name
Name                                                                 Kind   Scope
----                                                                 ----   -----
Append CostCenter - PPC tag and its default value to resource groups policy /subscriptions/<this-subscription>
RG - PPC CostCenter                                                  policy /subscriptions/<this-subscription>

The initiative is gone, but two of the custom policies that probably made it up are still there. So version 1.1 can’t be rebuilt from its own export: assign it again and the tagging part would point at nothing. Version 1.0 only uses built-in policies, so that one would still work.

Missing policy objects throw an error

In Az.Resources 10, Get-AzPolicySetDefinition -Id throws “could not be found” for a missing initiative, even with -ErrorAction SilentlyContinue. Wrap it in try/catch if you want to check whether something exists.

PPC was the name of an all-in-one endpoint solution we were setting up two companies back. Intune did the configuration, and Azure did the rest: Key Vault for the secrets and certificates our runbooks needed, an Automation account for the runbooks we could push to machines, and Log Analytics to store the data. So the one blueprint I built was already for endpoint work, right at the point where I was moving from Azure to Intune.

The templates show their age too. The API versions run from 2015-01-01-preview (Automation) to 2018-07-01 (Storage), and the Key Vault and Log Analytics templates put classic resource locks on their resources, separate from anything Blueprints locks itself.

With both versions exported and nothing assigned, there was no reason to keep it around until Microsoft deletes it. Az.Blueprint has no Remove-AzBlueprint, so my script calls the REST API with Invoke-AzRestMethod: published versions first, then the definition. It refuses to run if the blueprint is still assigned or a version hasn’t been exported, and supports -WhatIf. The core of it:

$base = "/subscriptions/$($sub.Id)/providers/Microsoft.Blueprint/blueprints/DefaultBlueprint"
$api = 'api-version=2018-11-01-preview'

foreach ($version in $blueprint.Versions) {
    Invoke-AzRestMethod -Method DELETE -Path "$base/versions/$($version)?$api"
}
Invoke-AzRestMethod -Method DELETE -Path "$base`?$api"
== Deleting DefaultBlueprint from DemoSubcription

Confirm
Are you sure you want to perform this action?
Performing the operation "Delete" on target "DefaultBlueprint version 1.1".
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): a
version 1.1  deleted
version 1.0  deleted
definition   deleted

== Blueprint definitions left on DemoSubcription
none

Running the first script again right afterwards:

== Blueprint definitions visible from DemoSubcription
none

== Blueprint assignments on DemoSubcription
none

== Advisor retirement recommendations for DemoSubcription
Feature  : Azure Blueprints
Retires  : 2027-01-31
Impacted : 1.0
Resource : /subscriptions/<this-subscription>/providers/microsoft.blueprint/blueprints/defaultblueprint/versions/1.0
Problem  : Retirement notice: migrate from Azure Blueprints by January 31, 2027

Feature  : Azure Blueprints
Retires  : 2027-01-31
Impacted : 1.1
Resource : /subscriptions/<this-subscription>/providers/microsoft.blueprint/blueprints/defaultblueprint/versions/1.1
Problem  : Retirement notice: migrate from Azure Blueprints by January 31, 2027

Feature  : Azure Blueprints
Retires  : 2027-01-31
Impacted : defaultblueprint
Resource : /subscriptions/<this-subscription>/providers/microsoft.blueprint/blueprints/defaultblueprint
Problem  : Retirement notice: migrate from Azure Blueprints by January 31, 2027

The blueprint is gone, but Advisor hasn’t noticed yet: it still lists all three, pointing at resources that no longer exist. Advisor refreshes its recommendations on its own schedule, so don’t take a recommendation that lingers as proof the delete didn’t work. Check the definitions themselves.

So if the mail landed in your inbox too: check before you file it as noise. An unassigned definition like mine is harmless, but an assignment with locks loses those locks on January 31.

The Advisor view misses some

The Advisor list works per subscription, so a blueprint definition saved at a management group that isn’t assigned anywhere doesn’t show up there. The Blueprints portal does list those.

The resources a blueprint deployed stay. What goes away is everything around them:

  • The locks. Blueprint locks (“Do Not Delete” and “Read Only”) are deny assignments, and Microsoft removes them all at retirement. Anything that relied on them, such as stopping a subscription Owner from deleting a resource group, stops being enforced. The retirement FAQ puts it plainly: “any policy/permission enforcement that depended on Deny Assignments is gone.”
  • The definitions. Definitions, versions and assignments you haven’t exported are deleted automatically.
  • The tooling. Az.Blueprint and the az blueprint CLI extension stop working; both already print a deprecation warning on every command.
Locks after December 31

You can switch a blueprint’s locks off by setting its assignment to “Don’t Lock”. From December 31, assignments can’t be changed anymore, so after that the only way to get rid of the locks early is to delete the assignment.

Roughly in this order, while reading and exporting still work:

  1. Export the definitions, every version. Export-AzBlueprintWithArtifact writes a Blueprint.json plus an Artifacts folder per definition (docs). It exports one version at a time, so loop over them:

    $bp = Get-AzBlueprint -SubscriptionId $sub.Id -Name 'DefaultBlueprint'
    foreach ($version in $bp.Versions) {
        $published = Get-AzBlueprint -SubscriptionId $sub.Id -Name $bp.Name -Version $version
        Export-AzBlueprintWithArtifact -Blueprint $published -Version $version -OutputPath ".\export\$version"
    }

    Also export anything the artifacts point to that lives outside the blueprint, like custom policy definitions and initiatives.

  2. Capture the assignments yourself. The export only covers definitions. Parameters, lock mode and excluded principals live on the assignment, so save Get-AzBlueprintAssignment output as JSON next to the export.

  3. Rebuild it as Bicep or ARM, version it as a template spec or simply in Git, and deploy it with a deployment stack (next section).

  4. Remove the assignment last. The locks go with the assignment, but don’t count on everything else staying behind. Microsoft’s migration skill warns that “removing a Blueprint assignment can remove embedded policy assignments”, while role assignments it created often stay. So deploy the replacement first, and check afterwards what’s still there. az blueprint assignment delete --delete-behavior all also deletes the resources the assignment created, so be careful with that one.

For the definitions themselves, there’s no Remove-AzBlueprint cmdlet. Use az blueprint delete or the REST API, as in my cleanup.

That migration skill is an unofficial GitHub Copilot skill in the Azure/azure-blueprints repo. It isn’t a converter, but its notes on mapping locks and adopting existing role assignments are worth reading.

Microsoft splits the replacement in two:

Blueprints had…Today you use…
Definitions and versionsTemplate specs, or just Bicep in Git
Assignments, lifecycle and locksDeployment stacks
Lock “Do Not Delete” / “Read Only”Deny settings DenyDelete / DenyWriteAndDelete

A few things I’d want to know before relying on stacks, from the known issues:

  • No what-if for stacks. You can run New-AzSubscriptionDeployment -WhatIf on the underlying template instead.
  • Five excluded principals at most in the deny settings, so exclude a group rather than individual people.
  • Deny settings don’t work at management group scope itself; a management group stack that targets a subscription is fine.
  • -ActionOnUnmanage decides what happens to resources you remove from the template. Microsoft’s own template spec walkthrough uses DeleteResources; while you’re still moving things over, DetachAll is the safer choice.

So what would I use if I built PPC again today? Bicep, deployed as a plain deployment or as a deployment stack, versioned in Git, for the resources. And the policy side through EPAC, the policy-as-code approach I wrote about in Azure Policy management done right. Not one package like Blueprints promised, but each part handled by the tool that’s best at it.

In a sense I’m kind of sad I didn’t get to play with Blueprints more, but the reality is that the tools I’d use now—Bicep, deployment stacks, and EPAC—are more flexible and better suited to modern infrastructure-as-code practices.

It still took them long enough to realize that Blueprints wasn’t the right long-term solution, but at least the alternatives are now clear and well-supported.

All in all an email that surprisingly took me back to memory lane of what was the next big thing in Azure governance, and how quickly the landscape can change.

Happy governance! 😉