Azure VM Reservations

Azure Reservations are a good way to reduce your cloud costs. Reservations are available for SQL, Databricks, storage Virtual Machines, and many other services. But in this topic I will be looking into Virtual Machines only. The reason for that is that these (although amongst others) are a; the most used, b; can be exchanged easily and finally c; might be complex.

So, what is a reservation. A reservation allows you to “reserve” some compute to reduce the cost for VM’s in Azure that you are going to use for longer term. Let’s take an example;

Calculating the discount level PAYG pivot point:

Say, we have a D4S_v3 VM running in Azure – and it will be running for a very long time, let’s say 2 years. In the normal pricing scheme you would pay; ~$170 per month, or around $4080 for those 2 years in total. But what if we used a reservation?

Reservations are available in 1 and 3 year options, the 1yr option drops the price by about 32% to $116/month or $2784 for 2 years (if the 1-yr is extended to the 2nd year). So, saving $1296 in total.

Now, what if you don’t run this VM 24/7 for those 2 years. Perhaps you just have a project that runs for only 1.8 years. In that case, a reservation might still be cheaper. As the discount is applied as a percentage, this means we can calculate pivot point on which the VM becomes cheaper.

In our case, the difference for those 2 years was $1296 total, which in percentage is:

1296 / 4080 * 100 == 31.76% in the image below we can see that is April/May-ish in the 2nd year.

You can also decide to shutdown the VM in the weekends to save cost. In that case, we’d be assuming the VM runs for the full 24 months. Within that 2-year span, the VM will not operate for 2 days in the weekend (2x 52 = 104 days) which comes down to 2496 hours out of the (2x 365 x 24 hours available in the timeframe):  17.520 hours, which comes down to about 14%, still within the limits of the reservation discount of 31%:

Conclusion: If you shutdown the VM for 2 days in the weekend during that 2 year period, a reservation is going to be cheaper.

Let’s say however we decide to run the VM 8 hours per day (shutdown in the night). In that case we can calculate it rather quickly. 8 hours is 1/3 of the available time per day (24/3=8 == 33.3%). In that case, the reservation providing 31% discount will not be cheaper.

If you have a fixed discount on your Azure bill through whatever means, that calculation obviously becomes a bit trickier. To help you, I’ve written a PowerShell module that you can use. The cmd’let (Get-AzVMPrices) takes the ACRDiscount as percentage as an input, receives the public prices from the Microsoft API and calculates the pivot point based on months in the PAYGdiscounted, 1YR RI and 3YR RI known values per region. If needed, you can also limit the output to only the available VM’s in your subscription. You can find the PSM1 here: RZomermanMS/AzurePrices (github.com)

Working with it:

#Import the module
import-module .\AzurePrices.psm1
$Prices=Get-AZVMPrices -ARCDiscount 5 -location uaenorth

The output will be an array in $Prices that you can alter to your needs or even export to a file.

$prices | select meterName, productName, PAYG, DACR, 1YR, 1Y%, 3YR, 3Y% | sort-object -property meterName | Sort-Object metername -Unique | where {($_.metername -notmatch 'Spot') -and ($_.metername -notmatch 'Priority') -and ($_.'1Y%' -ne 0) -and ($_.productName -notmatch 'Windows')} |ft

Note: I filtered out the Windows SKU’s as the RI prices for Windows are based on compute only. Additional cost will be added to those SKU’s for the Windows licenses.

Reservation scope

Reservations are not 1:1 tied to the actual VM you want to apply the reservation to. Each reservation has a scope, and you can choose from the following:

  • Shared
    All Virtual machines within scope in all subscriptions under a single billing unit
  • Subscription
    All Virtual machines within scope in a subscription
  • ResourceGroup
    Virtual machines within scope in a resource group

That means that if you run 100x DS2_v2 VM’s and you reserved 80 of them over various subscriptions, the shared model will reduce the cost of 80 of them in total. Not specifically marked VM’s and thus also not a 1-to-1 relation.

This has upsides, such as being able to leverage the discount a much as possible, but it also can be seen as a disadvantage. Let’s say you have specific budgets for your project. You determine that with RI’s you get (say) a 50% discount on your VM’s. Someone puts the RI’s on the Shared Scope and all of a sudden, one month you notice that the invoice for your project did not include the discount anymore but it was given to another project in another subscription or resource group.

That is why you can put it to Subscription / ResourceGroup mode, but then overall the company might be paying more when you are not utilizing your RI’s, and you will be billed for the non-usage of the RI’s asif your VM was running anyway.

If you overall pay the Azure bill as a whole and you don’t reimburse internally on individual project level, its best to set the RI to Shared, if you specifically work with Charge Backs or project-based invoices use subscription or Resource Group level. But be aware that in the last method, you might not be fully benefiting from your RI’s if VM’s are shutdown.

Family Sku’s

The discount for a reservation depends on the type of VM being reserved. When you make a reservation, you can select if that reservation is specifically for that type of VM, or if it can use what Microsoft calls “Instance Flexibility”.

Within Azure there are different VM types: A-series, D series and then even various versions of those, v2, v3, v4, etc. For reservations another differentiating factor exists as well. There are InstanceFamilySkuSizes. So, within a DS_v2 series, there are 2 different InstanceFamilySkuSizes: “DSv2 Series” and “DSv2 Series High Memory” (and there is even a Dedicated Host one so a total of 3 which we will leave out of scope for now). So when you enable this, make sure to understand the covered VM types in your reservation.

In the image above you could already see a subset of these InstanceFamilySkuSizes under the productName column. When you enable Instance Flexibility, the result is that the number of cores reserved (for all reservations in that scope) go into a bucket for that FamilySkuSize. There is a ratio as well (usually 1, 2, 4, 8 etc) for the number of cores reserved and used.

Let’s say we reserve 10x DS4_v2 and 80x DS2_v2 Series in various RI’s; because a DS2 has 2 cores and a DS4 has 4 cores, we essentially reserved 200 cores (40+160). Any VM in the same family (and within the scope of the RI’s) can allocate from this pool:

As indicated, the pool can be created from multiple RI’s and each VM within the scope of the RI (and Family SKU size) can use the pool. A larger VM will just consume more cores from the pool than a smaller VM based on the ratio defined in Azure. This ratio can be download at: https://isfratio.blob.core.windows.net/isfratio/ISFRatio.csv

But what happens if the pool is exhausted? Say we have the 200 cores in the RI pool for a given SKU Family and we are using 198 of them. Someone spin’s up a DS5_v2 consuming 16 cores: In short, 14 will be PAYG priced, and 2 cores will be allocated from the RI pool.

Using the flexibility size can help a lot in reducing overall cost, but be aware that in a shared RI that cost reduction can happen at any subscription, and any VM. Meaning, you get the most benefit that comes with the maximum flexibility in your invoice/cost management. One day, a VM in subscription 1 might use the RI, the next another for a whole different project in another subscription might use it. If the scope is set to subscription or even resource group, you might not get the most discount, but the return is a more static invoice for that subscription.

Instance Flexibility allows you to change the size of your VM (within the Instance Flexibility Family Group ) when needed while being the most cost effective and without the need to change the backing RI itself.

Effectiveness of your RI

You can check if your RI is effective or not – this as you will be paying for your RI regardless if it is actually used or not. Each RI has a usage % indicator and includes which VM’s at what time were using the RI. To make best use of your RI, you essentially want to ensure 100% usage for the duration of it. The below would be a very bad example of an implemented RI with only 29% actual usage.

Returns and Exchanges.

What if you committed, but changed your mind? You want to return or even swap the reservation? You can, although returning might come with a penalty according to the Microsoft documentation, exchanging them can be done without that (possible) penalty. Although – there is a hidden penalty in exchanging. Remember the ‘pool’ of cores that we can create? How we create that pool is secondary. We can reserve 100x DS2_v2’s or 50x DS4_v4’s. And then we make do those reservations in 1 go, or in 100/50 individual reservations. What is the difference? That becomes clear when we exchange.

Let’s say we ordered 1x 100 DS2_v2’s (200 cores total) on Jan 2020 for a 3yr RI worth ~$143k. Due to circumstances on Jan2021 we want to switch 50 of those to E2a_v4 series VM’s worth ~$147k. When you do an Exchange, the remaining amount reserved will be used as an offset to your new reservation, but the initial reservation will be cancelled, and 2 new reservations will be created.


RI1 (100x DS2_v2)   –       == 95.3k (2 years amount left) returned
RI2 (50x   E2a_v4)  +        == 147k (3 years) new reservation
RI3 (50x DS2_v2)   +        == 73.6k (3 years) new reservation
=========================================
To be paid:                          == 125.3k
(prices are estimates and rounded up for ease)

The result of this swap is indeed that we now have 50x DS_v2 and 50x E2a_v4, but both these new reservations have Jan 2021 as a start-date and thus an end-date of Jan 2024. By swapping them, you have renewed 50x DS2_v2 for the next 3 years (effectively extending the original one by 1 year) – if you use them, no problem, but if you had planned on decommissioning them at the original RI end-date (2023) you are overpaying, or you need to swap them again for something else. If you would have made 100 individual reservations, you could have exchanged only 50 of them without impacting the others.

Another thing to note is that the new reservation(s), should be greater or equal in terms of commitment (read $) – in short so no combination of refunds & exchange in a single switch.

How to find out what you need to reserve?

But while this is all nice and great, how do you know what to reserve? For this, the Azure Advisor can help. It has a special tab that shows which type of VM, in which location and which subscription you can reserve to optimize cost. The thing is, however, this report does not take into account size flexibility or shared RI’s. What if you want an overview of the reservations in a shared model per region? Then you run this (downloaded) report through a PShell script and that gives you the answers you are looking for. The original CSV looks like:

While I’ve used the CSV, you can also use the Azure Advisor API / AZ command to get the same results.

Using the following code, I extracted and matched the following information into my object:

#Download ISFRatio
Invoke-WebRequest -Uri https://isfratio.blob.core.windows.net/isfratio/ISFRatio.csv -OutFile ISFRatio.csv
$ISFRatio=Import-Csv .\ISFRatio.csv
$CSV=Import-CSV .\MyAzureAdvisor.csv
ForEach ($object in $csv) {
    $line=$object.'Resource Name'
    $size=($line -split " virtual machines in ")[0]
    $location=($line -split " virtual machines in ")[1]
    $locations.add($location) | Out-Null
    $Allsizes.add($size) | Out-Null
    $object | Add-Member -MemberType NoteProperty -Name "ArmSkuSize" -Value $size -Force
    $object | Add-Member -MemberType NoteProperty -Name "Location" -Value $location -Force
    $Index=$ISFRatio | where {$_.armSkuName eq $size} | select InstanceSizeFlexibilityGroup,Ratio
    $SkuFamily=$Index.InstanceSizeFlexibilityGroup
    $SkuFamilyRatio=$Index.Ratio
    $object | Add-Member -MemberType NoteProperty -Name "SkuFamily" -Value $SkuFamily -Force
    $object | Add-Member -MemberType NoteProperty -Name "SkuFamilyRatio" -Value $SkuFamilyRatio -Force
}

This results in a $CSV Array that I can then use to retrieve the information I want:

The output keeps the original ArmSkuSize for each VM, but also adds the SkuFamily and Ratio. This means that in the screenshot above, you can also reserve 3x F2S_v2 Series in North Europe to also cover the F4S_v2 recommendation. (1x F4S_v2 * 2 (ratio) + 1x F2S_v2 *1 (ratio) – in the FSV2 Series). Given the output of the CSV can now be altered in the way we want, we can actually filter on region, SkuFamily and perform calculations on them as well.

Conclusion

I hope the above has shown you that (VM) Reservations provides you with options to reduce your Azure invoice, it comes with benefits, but there are some things to take into consideration when you have a fast changing environment of VM (types).

Disclaimer

Azure contracts may vary, prices shown are estimates based on the Public Azure Pricing Calculator at time of writing.

Tagged