Use Azure Function to Clean-up your Mess, Automatically
Tags: azure, function, serverless, resourcegraph
Published: 2019-09-12 02:54:34

---
title: Use Azure Function to Clean-up your Mess, Automatically
published: true
description: How to base schedule, delete all resources that are "expired" using Azure Function.
tags: azure, function, serverless, resourcegraph
series: Serverless September
cover_image: https://thepracticaldev.s3.amazonaws.com/i/970hvonm3tctioxta6is.jpg
---

> This article is part of [#ServerlessSeptember](https://dev.to/azure/serverless-september-content-collection-2fhb). You'll find other helpful articles, detailed tutorials, and videos in this all-things-Serverless content collection. New articles are published every day — that's right, every day — from community members and cloud advocates in the month of September.

> Find out more about how Microsoft Azure enables your Serverless functions at [https://docs.microsoft.com/azure/azure-functions](https://docs.microsoft.com/azure/azure-functions/?WT.mc_id=servsept_devto-blog-cxa).

In a previous [post](https://dev.to/azure/keep-your-azure-subscription-clean-automatically-mmi), I explained how to created an Azure Automation to execute a PowerShell script that deletes all expired resources.  This time, we will use the power of serverless to clean-up our mess.

## The Goal

Base on a schedule, we want to delete all resources that are "expired". A resource will be qualified of expired when it first as a tag `expireOn` and second and the value of this tag is before the current day.

All the code is available in my [GitHub repo](https://github.com/FBoucher/AzSubcriptionCleaner). There is also a one-click button to deploy the solution in your Azure subscription!

## Azure Resource Graph 

Azure Resource Graph queries are extremely performant. You can read an [excellent post](https://www.codeisahighway.com/how-azure-resource-graph-is-gonna-change-the-way-you-search-and-script/) from Stephane Lapointe (Microsoft Azure MVP), that show some performance comparison. In short instead of retrieving the resourced and looping through them, we retrieve only what we are looking for; less record so faster. 

In our case all resource with a tag `expireOn` smaller than `now()`.

```powershell
$expResources= Search-AzGraph -Query 'where todatetime(tags.expireOn) < now() | project id'

foreach ($r in $expResources) {
    Remove-AzResource -ResourceId $r.id -Force
}

$rgs = Get-AzResourceGroup;
 
foreach($resourceGroup in $rgs){
    $name=  $resourceGroup.ResourceGroupName;
    $count = (Get-AzResource | Where-Object{ $_.ResourceGroupName -match $name }).Count;
    if($count -eq 0){
        Write-Output "==> $name is empty. Deleting it...";
        Remove-AzResourceGroup -Name $name -Force -WhatIf
    }
}
```

Then we need to delete the empty resource group, and because those are not searchable with Resource Graph we use the usual `Get-AzResourceGroup` to get all the groups and delete the empty one with  `Remove-AzResourceGroup`.

> To learn all about what you can do with Azure Resource Graph, check this excellent [documentation](https://docs.microsoft.com/en-us/azure/governance/resource-graph/first-query-powershell?WT.mc_id=servsept_devto-blog-frbouche), packed with samples. I also did a 5-minute video [Search Like a Boss with Azure Graph Query](https://youtu.be/0iOwUkOg6_c) where I explain how it works.


## The PowerShell Azure Function

In this post, I will use the great [Azure Functions for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions&WT.mc_id=servsept_devto-blog-frbouche) extension to create my function.

From the VSCode extension, click on the first option **Create New Project**. Select a local folder and a language; for this demo, I will use PowerShell. This will create a few files and folder. 

Now let's create our Function. From the extension menu, select the second option **Create Function**. Create a Time Trigger named **AzSubCleanerTimeTrigger**. The last question will be to provide a schedule using [cron expression](https://en.wikipedia.org/wiki/Cron#CRON_expression). To get the function to be trigger every morning at 5 am, enter `0 0 5 * * *`. You can change this at any time by editing the file `function.json`.

!["Create the PowerShell Function"][createFunction]

Add the PowerShell displayed previously at the end of the file run.ps1. There you have it our code is completed.  The function will receive the time in the parameter, that you can use, let's say for log purposes.

## Required Module

If you could wait and already try ton execute this function you probably got an error about a missing module. This is because to process the command `Search-AzGraph` we need to need to add the module `Az.ResourceGraph`. 

To add a PowerShell module to our FunctionApp create a folder named "modules" at the root. If you already have the module install locally you can find the location of the the the module folder by executing the following command: 

```powershell
Get-Module -ListAvailable Az.ResourceGraph
```
You need to copy the folder `Az.ResourceGraph` into the folder modules you just created. It should look like this.

!["Result Module folder"][module]

If you don't have the module installed locally, you can install it with the following command or check the more [complete documentation here](https://docs.microsoft.com/en-us/azure/governance/resource-graph/first-query-powershell?WT.mc_id=servsept_devto-blog-frbouche). 

```powershell
Install-Module -Name Az.ResourceGraph
```

## Deployment

To deploy the Azure Function with the VSCode extension, it's very easy. From the extension, click the third button *Deploy to Function App*. Select the current folder (you should be in at the root of our Function folder), and the destination subscription. When prompt to Select Web App, select the *+ Create new WebApp*, and type **AzSubscriptionCleaner** as the name. It should take a minute and the deployment will be completed.

It's mostly done, now we need to authorize the Azure Function to manage the Subscription. Once the deployment is completed, go in the Azure Portal and open the Azure Function App you just deployed. Open the **Platform features** tab, and select *Identity* in the Networking section.

!["Open Identity Property"][identity]

Now change the status to "On", and save. This will create a new principal and assign it to the Function App.

To assign the contributor role to this principal, we need to go in the subscription. From the left menu select *All Services* and type "Subscription" in the search bar. Click on **Subscriptions** to see the list of available subscriptions.  Click on the subscription where you just deployed AzSubscriptionCleaner.

!["Select Subscription"][selectSubscription]

From the option at the left select *Access control (IAM)*. Click *Add* and select *Add role assignment*. From the new panel on the left, Select *Contributor* as Role and enter the name of your resource (ex: azsubscriptioncleaner) in the *Select* textbox.

!["Assign Role"][AssignRole]

Select the *Select* blue button, and you are done!

## And Then?

Next morning, the Azure Function will get triggered and delete all expired resources. Go in the Azure portal and add a few tags. From any resource, click the **Tags** option and enter `expireOn` as Name, and the date you wish this resource to expire following the format `YYYY-MM-dd`.  
It's also possible to add tags with PowerShell, Azure CLI and from Azure Resource Manager (ARM) template. 

I also have a video version of this post, if you prefer.

{% youtube kbM8zwvUpkg%}

Happy cleaning!

*Credit to: [@iboonox](https://twitter.com/iboonox) for the amazing header image! Thank you.*

[createFunction]: https://thepracticaldev.s3.amazonaws.com/i/rm3rmp4qh78dhl5m099z.jpg
[selectSubscription]: https://thepracticaldev.s3.amazonaws.com/i/3nol2f9e8wb2vj87pyd1.png 
[AssignRole]: https://thepracticaldev.s3.amazonaws.com/i/vfiysyx5mnpd0kfpf2mq.png 
[module]: https://thepracticaldev.s3.amazonaws.com/i/hugguzrhenmm1lbu3vwv.png
[identity]: https://thepracticaldev.s3.amazonaws.com/i/on6857ug01necq77c8dw.png
[thumbnail-ep54]: https://thepracticaldev.s3.amazonaws.com/i/qu41aa3tqgjjxafby6jf.png