UPDATE. On May 23, 2023, Microsoft announced a built-in functionality for alerting about underused Azure Reservations. If you don't need your reporting in a specific format and are okay with a standard notification template from Azure, it's totally fine to use that option in your FinOps processes.

Using Azure Reservations is one of many technics to optimize your cloud spend in Azure. They are a great option to reduce costs for Azure resources with a long lifespan and predictable utilization. Have a VM, storage, or database that will run ‘permanently’? Buy a reservation for it, set it up to auto-renew upon expiration, and enjoy savings of up to 70% or so. Does it sound too good to be true? Let’s find out.

For small setups with few cloud resources or stable infrastructure with little changes, the easiness of using the reservations is mostly true. However, for large enterprise-scale environments, the reality is a bit different. The larger the environment, the more changes in it you inevitably have. Apart from that, the flexible nature of cloud services, which can be provisioned and decommissioned in a matter of minutes and not weeks or months, provides even more opportunities for engineers to modify their solutions and change their infrastructure requirements. Something intended to run for the next few years can be gone in a few months or replaced with other infrastructure components.

All those changes in a cloud infrastructure create the challenge of managing Azure Reservations efficiently. As the reservations are implicit commitments to pay for a fixed amount of cloud resources over one or three years, it’s essential to utilize those resources fully. Otherwise, you will be paying for resources you actually don’t consume, and all your savings from using those reservations will be diminished.

Luckily for us, Microsoft allows exchanging existing reservations for new ones. Of course, there are some limitations, but it’s still more optimal to return unused reservation units and buy new reservations for services that you do use. The obvious first question here is how you know what your reservations are underutilized and to what extent.

Azure Reservations utilization info

Microsoft provides us with various options to check reservation utilization after its purchase. You can check it on the Reservation blade of the Azure Portal, use the corresponding section in the Cost Management + Billing interface, or run some ready-to-use Power BI reports. Also, you can use Azure PowerShell and Azure CLI to get some reservation utilization details. Apart from that, you can utilize the Reservations insight preview feature of the Azure Cost Analysis.

However, all those options have a common disadvantage – none of them has built-in functionality to automatically notify or alert in the event when utilization for Azure Reservations drops below a certain value. Even though the data about reservation utilization is collected by the platform, and you can check it in the reservation details on the portal, it’s not available as a metric in Azure Monitor.

One of the options to get the reservation utilization info programmatically is to use Microsoft Cost Management APIs. On the one hand, with such an API, you are not limited to vendor-provided monitoring options only, and you can use any custom or third-party monitoring solution that can query a REST API, parse a JSON payload, and extract the average utilization percentage value from it to be compared with some threshold.  On the other hand, something that could be a simple Azure Monitor metric alert requires a whole monitoring infrastructure for that and overcomplicates the monitoring setup.

Monitoring Azure Reservations utilization with Power Automate

As a decrease in Azure Reservations utilization is not something that you usually need to act upon immediately, it might make more sense to build an automated reporting that would provide you with insights about underused reservations on a regular basis related to your FinOps processes. So, by the time you are reviewing your monthly cloud invoices and analyzing your cloud spend, you have the data about what Azure Reservations require assessment and possible exchange or cancellation.

To build such a solution, I decided to try using Power Automate cloud flow for it. Apart from being user-friendly, it’s also usually available for most Microsoft customers as a part of their Microsoft 365 plans or as Logic apps in their Azure subscriptions.

Alternatively, you can implement a similar logic using Automation Runbooks or Azure Functions. My choice of a Power Automate flow (aka Logic app) here is merely a matter of quickly prototyping a solution that can be maintained and modified by people with no specific programming knowledge.

For a start, we can use the mentioned Cost Management APIs to get the list of all reservations with their usage summary, and the HTTP action can help us with that:

Be sure to use the correct enrolment number in the URI, which you can check on the Azure Portal or the Enterprise Portal (for EA customers only). Also, the API authentication might be tricky here, as you need to generate/get the API Access Key on the Enterprise portal first.

Then, you should provide that key in the specific format in the request Authorization header.

The successful API call will result in a JSON array, provided that you have some Reserved Instance purchases within the target enrollment.

Depending on your needs, you can switch to the daily grain when querying the API. However, from the practical point of view, it’s rather suitable for an in-depth analysis than for regular monitoring/reporting.

To parse that JSON output into an array we can manipulate, we can use the Parse JSON action, which is a common data operation in Power Automate:

The parsing requires a schema for understanding the structure of a JSON payload. You can generate it by yourself from your sample Reserved Instance usage details payload or use the following schema, which I already prepared:

The resulting array will contain the details for all your Reserved Instance purchases regardless of their utilization levels. Putting all that data into your utilization report might be unnecessary, as we are primarily interested in the reservations that are not fully utilized. In other words, the reservations with less than 100% utilization. However, as I explain next, there is not always necessary to achieve full utilization to benefit from the savings Azure Reservations provide.

Bad or good, there is no one universal threshold for reservation utilization that can be considered a best practice. That’s because, depending on the reservation terms, the savings from its usage may vary greatly. For example, in one case, the savings from using Azure Reserved VM Instances purchased for one year for a specific Azure VM SKU or instance flexibility size can be approximately 40%. In another case, purchasing reserved instances for a three-year term for the same Azure VM size(s) can result in more than 60% savings if utilized to the full extent.

Technically, if the utilization of your reservation with 60% projected savings drops by 50%, you are still saving 10% using it compared to on-demand costs. In contrast, if the utilization of the 40% reservation drops by the same amount, you are in the red.

Of course, that doesn’t mean you should target for such low savings by applying Azure Reservations. For analysis purposes, you can rely on the guidelines provided by Microsoft on the Reservation blade of the Azure Portal:

For the sake of this demo, let’s use the 90% utilization threshold to filter the reservations that require our attention. The Filter array action is to help us here:

The avgUtilizationPercentage property is provided as a float, so be sure to use the corresponding expression to compare it with your value.

Next, the resulting array will still contain the data about reservation utilization details for past months that might have little value for us if we already took some actions on them previously. It would be better to focus on last (or current) month, depending on what day of the month you run this report.

In my case, I run the report on the first day of each month, so it makes sense to see the reservation utilization details for a previous month only:

You can achieve that by filtering the usageDate property and using the following expression to get last month’s value in the same format as in the API response:

addToTime(utcNow(), -1, 'month', 'yyyy-MM')

Optionally, if you want to make your report cleaner, you can remove unnecessary data and select only properties you want to display in the resulting set with the Select action:

Now, when your resulting list of Azure Reservations is ready, you can deliver it to your target audience.

To keep it simple, we will send the flow output as an email notification. For that, we need to convert the resulting list into an HTML table using the Create HTML table action and then insert the formatted table into the message body:

Lastly, to put the reporting on autopilot, you can set it to run on a schedule using the Recurrence trigger:

Unfortunately, there is no easy way to schedule the trigger for a specific day of a month yet, so you can set it to execute daily with the following trigger condition:

@equals(utcNow('dd'), '01')

The final flow structure:

The result of your efforts might look like in the following example:

In conclusion

Monitoring your Azure Reservations utilization is really important as it explicitly correlates with the numbers in your cloud invoices. It should be a regular part of your FinOps practices in Azure, along with analyzing opportunities to purchase new Azure Reservations for the services in use. While Azure Advisor can suggest which reservation to purchase, the ‘rebalancing’ of existing ones is completely up to you. Having some food for thought, like the list of Azure Reservations you don’t use fully and the list of new reservations to purchase is the first step in the optimization process.

If you have questions about using Azure Reservations or would like to see more content about Azure FinOps practices in general, let me know in the comments! 👇