Azure Advisor - Known limitations of recommendation suppressions


Intro

This article is part of a series: Navigate to series page

Before diving into the how-to parts of this series, here are the practical limitations I’ve run into when managing Azure Advisor recommendation suppressions with Bicep and PowerShell/az CLI. Knowing these up front will save you a few surprises when you design your pipeline, so I’m covering them first.

Suppressions and recommendations are always subscription-scoped operations

The list operations for both recommendations and suppressions only exist at the subscription level:

GET /subscriptions/{subscriptionId}/providers/Microsoft.Advisor/recommendations
GET /subscriptions/{subscriptionId}/providers/Microsoft.Advisor/suppressions

There is no management group level or resource group level list endpoint. If you need a tenant-wide or management-group-wide view (for a dashboard, a documentation export, or a compliance check), your pipeline has to enumerate every subscription in scope and call the API once per subscription - Advisor won’t aggregate this for you.

This is why the script in Part 2 - Get current Advisor suppressions is written per-subscription; wrap it in a loop over Get-AzSubscription if you need broader coverage.

The individual suppression itself is scoped to a specific resource

Even though the list call is subscription-wide, an individual suppression is created against a specific resourceUri - which can be the subscription itself (for subscription-level recommendations like reserved instance cost recommendations) or a specific resource (like a VM, for resource-level recommendations like high availability). This is the parent you reference in Bicep, either with targetScope = 'subscription' and no extra scope, or with scope: <resource> for a resource-level recommendation - see the two variants in Part 3 - Create a suppression with Bicep.

Suppressions do not inherit to child scopes

A suppression is a 1:1 relationship between a specific recommendation instance and the exact resource it was created against. It does not cascade:

  • Suppressing a recommendation at the subscription level does not suppress the same recommendation type when it later appears against an individual resource within that subscription.
  • Suppressing a recommendation on one resource does not suppress it on other, similar resources - even identical VMs in the same availability set each get their own recommendation instance and need their own suppression if you want them all silenced.

If you want to silence a whole class of recommendations across many resources, you need to create (and maintain) a suppression per resource, or use Advisor configuration to exclude specific subscriptions/resources from generating certain recommendations in the first place - which is a different mechanism from suppressions and out of scope for this series.

The recommendation ID is not permanently stable

The recommendationId you suppress against is the identifier of a specific, cached recommendation instance (not always formatted as a GUID - it can also be a long hex hash, depending on the recommendation type). Advisor periodically recalculates recommendations, and when it does, a recommendation can be regenerated with a new identifier (for example after the underlying resource configuration changes, or after enough time has passed for Advisor to reassess it). A suppression created against an old, now-regenerated recommendation ID has nothing left to attach to.

Design implication: never hardcode a recommendationId as a literal value in your Bicep parameters file for the long term. Resolve it dynamically at deployment time (as shown in Part 3), and re-run that resolution step periodically to confirm the suppression is still attached to a live recommendation.

No native Bicep delete without deployment stacks

As covered in Part 5, a standard incremental-mode Bicep deployment never removes a resource just because you deleted it from the template. To get IaC-native deletion, you need either a complete-mode deployment (risky at any shared scope) or an Azure Deployment Stack with resource cleanup enabled. Otherwise, plan on calling the REST DELETE operation directly as an explicit pipeline step.

TTL is a plain TimeSpan string, not an enum

The ttl property accepts a .NET TimeSpan-formatted string (d.hh:mm:ss, for example 90.00:00:00 for 90 days). The Azure portal’s Postpone dialog only exposes a handful of preset durations (1 day, 1 week, 1 month, 3 months), but the API itself does not restrict you to those presets - any valid TimeSpan string is accepted. There’s no dedicated “forever”/permanent suppression value documented for the API; if you need an effectively permanent suppression, use a long TTL (for example a year or more) and make sure your renewal pipeline (from Part 4) picks it up before it expires.

Permissions follow the resource, not a dedicated Advisor role for suppressions specifically

To manage recommendation status (which includes creating/updating/deleting suppressions), the built-in roles that grant this are Contributor, Owner, or Advisor Recommendations Contributor (Assessments and Reviews) - at whichever scope (resource, resource group, or subscription) the recommendation applies to. Plain Reader roles can view recommendations and suppressions but cannot change them. Make sure your pipeline’s service principal has one of the write-capable roles at the correct scope - see Azure Advisor permissions for the full breakdown.

Summary

LimitationPractical impact
No management group/resource group list endpointLoop over subscriptions yourself for broader visibility
Suppression tied to one resource, one recommendation instanceNo bulk/class-wide suppression; one resource needs one suppression
No inheritance to child scopesSubscription-level and resource-level suppressions are independent
recommendationId can change over timeResolve dynamically at deploy time; don’t hardcode
No native Bicep delete (incremental mode)Use deployment stacks, or call DELETE directly
ttl is a free-form TimeSpan stringNo enforced maximum; build your own review/renewal cadence
Permissions scoped to the resourceGrant Contributor/Owner/Advisor Recommendations Contributor at the right scope

Final remark: treat Advisor suppressions as short-lived, reviewable exceptions rather than permanent configuration. Pairing the Markdown export from Part 2 with a scheduled pipeline review (from Part 4) is the pattern that has worked best for me to keep suppressions from silently accumulating and hiding real recommendations indefinitely.

With these limitations in mind, let’s start with the practical side: getting a full inventory of what’s already suppressed.

Azure Advisor - Get current recommendation suppressions and export to Markdown