Delete play-resources from Azure using runbooks

Frederique Retsema

Learning a new cloud can be frighting: do I configure this right? And, when I click here, don’t I end up with a huge bill at the end of the month?

In general, these fears can be solved quite easily: when you follow along with a training, the trainer will tell you when something might cost you a lot of money. Some common sense is also required. When you start a virtual machine to play around with all the options that are there, you don’t need a virtual machine with 416 cores (even when they are available). In general I use the B1s (1 CPU, 1 GB memory, $6.40 per month) for Linux machines and the B2s (2 CPUs, 4 GB memory, $25.61 per month) for Windows systems. Most resources will be billed per hour, per minute or even per second – so when you stop all resources at the end of the day your bill will be reasonable.

Most of you will know the trick of creating a new resource group for tests and then remove that resource group at the end of the day. You have to keep in mind to actually do so and also be careful not to remove other resource groups that should stay. In my environment, I have a (test) DNS domain that I use to explore f.e. custom domains in Azure Active Directory. The resource group that this DNS domain is running in should not be removed at the end of the day.

To make the removal process more easy, I always test in the same resource group. It is called HelloWorld. At the end of the day, I remove all resources from this specific resource group, but I keep the resource group itself. This will not cost any money. And when I learned about runbooks, I thought “why wouldn’t I schedule this every evening, just in case I forget to remove the resources manually”? You can follow along in your own account when you wish to.

Automation Account

First, create an empty resource group called HelloWorld. Then go to the top of the portal and search for Automation Account. Create a new automation account in a resource group other than “HelloWorld”, call it f.e. Automation. Create a new Azure Run As account, this is the account that the playbook will use to execute the tasks. Please note the information box that warns that you might want to restrict the permissions of this account: it will be contributor on subscription level by default.

When you created the account, click on the left menu under “Shared Resources” on “Modules”. We will go and use Powershell. The disadvantage of runbooks that use Powershell is that they still use the old version of the Azure modules in Powershell. So we will have to add some modules to use the new version. For our solution, we need two modules: Az.Accounts to be able to connect to the Azure environment and Az.Resources to get information about resources in the HelloWorld resource group and to remove them.

To add these modules, click in the top menu on “Browse gallery”:

In the search bar, search for Az.Accounts. Click on Az.Accounts, then on Import. You will see a warning that importing a module may take several minutes. Click on Ok. You will see that the import of Az.Accounts from the portal is very fast, two seconds at the most.

Now try to add Az.Resources in the same way. At first, you might see the following message:

The confirmation that the Az.Accounts module was imported came too fast, Azure was still adding it in the background. When you try again after a minute or so you will see that it is now possible to import Az.Resources as well.

Runbook

Now go back to the Automation Modules page and in the left menu, under Process Automation, click on Runbooks. Add a new runbook by clicking on “Create a runbook” in the top menu. Name it “RemoveHelloWorldResources”, then click on Create.

Now you can copy the Powershell from below and paste it into this screen.

# Runbook RemoveHelloWorldResources
# =================================

# Constants
# =========

$AzureRunAsAccountName = "AzureRunAsConnection"
$resourceGroupName     = "HelloWorld"

# Connect-AzEnvironment
# ---------------------

function Connect-AzEnvironment {
    param(
        $AzureRunAsAccountName
    )

    Write-Output "TRACE: Connecting..."

    try
    {
        # Get the connection "AzureRunAsConnection "
        $sPConnection=Get-AutomationConnection -Name $AzureRunAsAccountName
        Connect-AzAccount `
            -ServicePrincipal `
            -Tenant $sPConnection.TenantId `
            -ApplicationId $sPConnection.ApplicationId `
            -CertificateThumbprint $sPConnection.CertificateThumbprint
    }
    catch {
        if (!$sPConnection)
        {
            $ErrorMsg = "$AzureRunAsAccountName not found."
            throw $ErrorMsg
        } else{
            Write-Error -Message $_.Exception
            throw $_.Exception
        }
    }
}

#  ====
#  Main
#  ====

Write-Output "TRACE: Start runbook RemoveHelloWorldResources"

Connect-AzEnvironment -AzureRunAsAccountName $AzureRunAsAccountName

Write-Output "TRACE: Get a list of resources to remove..."
$resourcesToBeRemoved = (Get-AzResource -ResourceGroupName $resourceGroupName)
$numberOfResourcesToBeRemoved = ($resourcesToBeRemoved | Measure-Object).Count

Write-Output "TRACE: Number of resources to be removed = $numberOfResourcesToBeRemoved"

while ($numberOfResourcesToBeRemoved -gt 0)
{
    Foreach ($resource in $resourcesToBeRemoved) {
        Write-Output "TRACE: $($resource.Name) will be removed..."
        Remove-AzResource -ResourceId $resource.ResourceId -Force
    }

    # Wait a few seconds for the last action to complete. When this isn't done, some parts
    # of Azure might still see the (already deleted) objects

    Start-Sleep -seconds 10

    Write-Output "TRACE: Get a new list of resources to remove..."
    $previousNumberOfResourcesToBeRemoved = $numberOfResourcesToBeRemoved
    $resourcesToBeRemoved = (Get-AzResource -ResourceGroupName $resourceGroupName)
    $numberOfResourcesToBeRemoved = ($resourcesToBeRemoved | Measure-Object).Count

    if ($numberOfResourcesToBeRemoved -eq $previousNumberOfResourcesToBeRemoved) {
        Write-Error "ERROR: Resources cannot be removed. Current list:"
        ($resourcesToBeRemoved).Name
        $ErrorMsg = "ERROR: There are remaining resources in resourcegroup $resourceGroupName."
        throw $ErrorMsg
    }
    Write-Output "TRACE: New number of resources to be removed = $numberOfResourcesToBeRemoved"
}

In the top menu, click on “Test Pane”. In this pane, click “Start” to test the runbook. As expected, there are no resources that can be removed.

Test with a Virtual Machine

Now, let’s test the runbook when a Virtual Machine is deployed. Create a new Virtual Machine in the resource group Hello World. Call it vm1, use an Ubuntu virtual machine, with size B1s. Use password as authentication type, you might want to use azureuser as user-id and enter “BlogAzure2021# as the password. In the Disks submenu, use Standard HDD as disk type (we will not log on to the machine anyway). Under Management disable the Boot diagnostics. Use the defaults for all other fields. Then click Review + Create and Create.

When the VM is deployed, go back to the Automation account, go to the runbook RemoveHelloWorldResources and click on Test pane again. Click on Start. You will see something like this:

You can see the dependency error: it is impossible to remove a disk when the VM is still running, so in the first while loop six of the seven resources are removed. The second time the VM is already gone and then the disk can be removed without any problems. When you look in the HelloWorld resource group, this resource group is empty. This looks fine, so in the Edit PowerShell Runbook screen, click on Publish and then press Ok.

Automation

You can now go to the runbook and press Start every time you want to clean up the HelloWorld resource group. But this isn’t too useful, because going to the HelloWorld resource group and delete all resources from there is just as quick. The real advantage of using Runbooks is that you can automate them. In our case, by scheduling this runbook to run every night at 10PM.

Go to the runbook “RemoveHelloWorldResources” and click on “Link to schedule”. Click under Link a schedule to your runbook and add a schedule. Name it “RemoveHelloWorldResourcesSchedule” and enter the data. Let it run recurring, every day without expiration.

I also added a schedule that runs once, at 10:00 AM (which is 30 minutes from the moment I am configuring this). You can find the schedules in the left menu of the runbook:

You can test the schedule (again) by creating a new virtual machine in the HelloWorld resource group. The results can be seen in the Jobs menu (under Resources).

Pricing of automation

You might have noticed the nice blue box “Learn more about Automation pricing” when you created the automation account:

Unfortunately, this is just an information box and it doesn’t include a link to a website. When you search for it on the internet, you will find this website [1]: in West Europe you currently get the first 500 minutes for free and pay $0.002 per extra minute.

Links

[1] Pricing of automation: https://azure.microsoft.com/en-us/pricing/details/automation/

Photo by Daniele Levis Pelusi on Unsplash

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Next Post

OWASP ZAP: A quick introduction to a versatile open source DAST tool

OWASP ZAP (Zed Attack Proxy) is an open source dynamic application security testing (DAST) tool. It is available here and has a website with documentation here. I recently encountered it when looking for open source security test tools to embed in a CI/CD pipeline (here). I was surprised by how versatile this tool […]