Azure Spring Clean: Enforce Budgets with Cost Management and Logic Apps

It’s that time of year again, time for Azure Spring Clean. The annual event organised by Joe Carlyle and Thomas Thornton to encourage you to look at your Azure subscriptions and see how you could manage it better. For my contribution, we will take a look at budgets in Azure Cost Management and how we can enforce them.

Spring Clean

Azure Cost Management is an excellent tool for keeping track of your Azure costs, and budgets provide a way for you to set boundaries on your spending per subscription or resource group. However, one of the limitations of Cost Management budgets is that you can’t enforce them out of the box. Budgets allow you to set a cost limit on a subscription or resource group, but once that limit is crossed, it’s only really going to send you some notifications. There’s no built-in way to restrict the resources or turn them off to enforce the budget.

However, Cost Management is hooked up to Azure Alerts. Besides sending out notifications via email or SMS, you can also trigger some other Azure services. In particular, you can trigger a Logic App. This is pretty powerful, as we can do a lot with a Logic App, including talking to Azure to apply some restrictions. In this article, we’ll look at how we can set this up.

Scenario

In this example, we’re looking to achieve a couple of things with budget actions:

  1. Send a Teams notification when we reach 80% and 90% of a budget
  2. When we reach 100% of the budget, apply a resource lock to the resource group to prevent any new resources from being applied or changes to any existing resources

Using a resource lock is a less disruptive process than turning things off. The existing service is still allowed to run and won’t impact users, but it’s disruptive enough to get the people running it to do something about their spending.

Create Logic App

The most complicated bit of this process is setting up the logic app. The first thing we need to do is receive the HTTP request that comes from the alert. The schema for the notification that is set is not well documented but looks something like this:

{
  "schemaId": "AIP Budget Notification",
  "data": {
    "SubscriptionName": "subscriptionName",
    "SubscriptionId": "<GUID>",
    "SpendingAmount": "100",
    "BudgetStartDate": "6/1/2018",
    "Budget": "50",
    "Unit": "USD",
    "BudgetCreator": "email@contoso.com",
    "BudgetName": "BudgetName",
    "BudgetType": "Cost",
    "ResourceGroup": "resourceGroupName",
    "NotificationThresholdAmount": "0.8"
  }
}

Now that we know what that looks like, we can create a logic app with an HTTP trigger to receive the request. First, create a new logic app; you can use the consumption plan to keep the cost down.

Logic app

Once the logic app has been created, we want to enable managed identity, which we will use to authenticate to the Azure REST API later. To do this, go to the Identity tab, and turn the system-assigned managed identity on.

We then need to assign permissions to this managed identity to add a management lock to any resource groups. You’re best off applying this at the subscription level. Currently, the only permission that can add resource locks is owner.

Once that is done, open up the designer and select the “when a HTTP request is received” option. When it opens the designer, you should see the new trigger event. We need to populate the JSON body, so click the “Use sample payload to generate schema” and paste in the JSON from above.

HTTP Request

Next, we need to add a condition statement to decide if we are between 80-90% of the budget, and so we send a team’s notification, or at 100% and want to lock the resource group. Click the plus button to add a new option, then search for “Condition” and add this. We now need to add our first condition, which checks if the threshold value is between 80% and 100%, but less than 100%, in which case we send a teams message.

The condition we want to use is the NotificationThresholdAmount from the HTTP request. However, if we use this, it will be treated as a string, and we can’t make logical numerical comparisons on it. We need to use the Float() function in logic apps to convert it. The expression will look like this:

float(triggerBody()?['data']?['NotificationThresholdAmount'])

This value contains the percentage of the budget that has been reached. Select “is greater or equal to” in the next box, and the value should be 0.8. Then add another row and repeat but set the condition to “less than” and the value to 1. Finally, ensure “And” is set in the drop-down, and you tick the box next to each row. It should look like this:

In the “True” box, we will send the team’s message. Click the plus box here, then search for “Microsoft Teams”. This will ask you to authenticate to Teams, and once you are done, you can select the “Post message in chat or channel”, then choose the channel you want to post to. Construct a message using the JSON values to provide the information you need.

Teams notification

In the false box, we need to add another condition to check the threshold value is 100%; just in case we get some messages with a threshold value of less than 80%, we want to ignore those. Do the same as you did in the previous condition but set it to look for a value equal to 1.

Condition 2

In the true box, we need to trigger the event that will apply the resource lock. To do this, we will use the HTTP connector, which will allow us to send a request to the ARM API to apply the resource lock. The HTTP connector will enable us to call any REST API, but it also has some logic built in to authenticate with Azure using the system-assigned managed identity we created earlier.

Click the add action button and search for HTTP. Click on the HTTP connector and then click the plain HTTP option. We’ll set up auth first. Click on the “add new parameter” option at the bottom and select authentication. In the drop-down that appears, select “Managed Identity”, leave it with System Assigned Managed Identity set, then in the audience add “https://management.azure.com”.

Auth

Next, we’ll set up the rest call. To create a management lock at the resource group level, we need to send a PUT request t

https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Authorization/locks/{lockName}?api-version=2016-09-01

As you can see, we need to substitute a few variables for those that came in on the HTTP trigger, the resource group and subscription. We also need to give it a name. We can add this to the URI field.

Set URl

Finally, we need to set the body value, which is some simple JSON to set the resource lock level (in our case read-only) and a note to help someone who sees this lock understand why it has been applied.

{
    "properties": {
        "level": "ReadOnly",
        "notes": "This resource is locked due to breaching the budget. Please contact your administrator "
    }
}

The final tasks should look like this:

Save the logic app. If you go back to the trigger, you should now see a URL for the trigger. We can use this for testing the logic app.

Testing

Testing this with an actual budget is tricky as you’d need to get one setup that can breach the required values easily. Instead, we can call the logic app URL directly using PostMan and send the data in the correct format. Open PostMan (or other REST clients) and create a new POST request with the URL you copied from the logic app. Select “raw” for the body type and set the content type to JSON. In the body, use the JSON below, replacing the values in “<>” with the actual resource group and subscription details.

{
  "schemaId": "AIP Budget Notification",
  "data": {
    "SubscriptionName": "<subscriptionName>",
    "SubscriptionId": "<supscriptionId>",
    "SpendingAmount": "100",
    "BudgetStartDate": "6/1/2018",
    "Budget": "50",
    "Unit": "USD",
    "BudgetCreator": "email@contoso.com",
    "BudgetName": "BudgetName",
    "BudgetType": "Cost",
    "ResourceGroup": "<resourceGroupName>",
    "NotificationThresholdAmount": "0.8"
  }
}

Once we submit this, we can see a message sent to teams to say the budget has hit 80%:

Teams message

If we change the value to 1 and rerun it, we can see the lock being applied:

Lock applied

Create Alert Group

Next, we need to create an alert group. This is the functionality that links our logic app to the budget. An alert group is essentially a container of different actions to take when triggered. Go to the Azure Alerts product in the portal, then click create and then action group. Add a name and resource group for the action group in the window that opens. The resource group is just where the action group will be stored. It can be any resource group you wish.

Action Group

Next, go to the Actions tab. Here create a new action and select “Logic App”. This will open a window allowing you to choose your logic app.

Logic App Action

Your completed actions tab should look like this:

Action Tab

Click on review and create to create the action group.

Create Budget

Finally, we can create the budget for the resource group. Select the appropriate subscription, resource group and budget values.

Budget

The next screen is where we link our action group to the budget. Set which percentage values you want the alert to trigger in the Alert Conditions section, and then pick your action group in the drop-down. In the example below, we will trigger the logic app at 80%, 90% and 100%.

Action Budget

That’s it. Your automation has now been created. You can re-use the logic app and action group for any other budgets you want to set. Just point the budget at the same action group. The logic app will get the correct subscription and resource group details.