This article is part of #ServerlessSeptember. 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.
In a previous post, 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. 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 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()
.
$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, packed with samples. I also did a 5-minute video Search Like a Boss with Azure Graph Query where I explain how it works.
The PowerShell Azure Function
In this post, I will use the great Azure Functions for Visual Studio Code 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. 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
.
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:
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.
If you don't have the module installed locally, you can install it with the following command or check the more complete documentation here.
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.
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.
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.
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.
Happy cleaning!
Credit to: @iboonox for the amazing header image! Thank you.